Revert "Change return type of wxGetTranslation"

This reverts commit 02cff90601 because it
broke a lot of existing code which did anything that boiled down to

	const char* whatever = _("Some message");

because the pointer became dangling instead of pointing to the memory
used by the cached translated string.

See #26195.
This commit is contained in:
Vadim Zeitlin
2026-05-03 15:20:35 +02:00
parent f57b17ba5f
commit df0d6be9ca
5 changed files with 162 additions and 62 deletions
+6 -6
View File
@@ -210,16 +210,16 @@ public:
//
// domains are searched in the last to first order, i.e. catalogs
// added later override those added before.
wxString GetString(const wxString& origString,
const wxString& domain = wxEmptyString) const
const wxString& GetString(const wxString& origString,
const wxString& domain = wxEmptyString) const
{
return wxGetTranslation(origString, domain);
}
// plural form version of the same:
wxString GetString(const wxString& origString,
const wxString& origString2,
unsigned n,
const wxString& domain = wxEmptyString) const
const wxString& GetString(const wxString& origString,
const wxString& origString2,
unsigned n,
const wxString& domain = wxEmptyString) const
{
return wxGetTranslation(origString, origString2, n, domain);
}
+48 -44
View File
@@ -179,11 +179,10 @@ public:
wxString GetHeaderValue(const wxString& header,
const wxString& domain = wxEmptyString) const;
wxDEPRECATED_INLINE(
static const wxString& GetUntranslatedString(const wxString& str),
return str;
)
// this is hack to work around a problem with wxGetTranslation() which
// returns const wxString& and not wxString, so when it returns untranslated
// string, it needs to have a copy of it somewhere
static const wxString& GetUntranslatedString(const wxString& str);
private:
enum class Translations
@@ -275,9 +274,9 @@ protected:
// ----------------------------------------------------------------------------
// get the translation of the string in the current locale
inline wxString wxGetTranslation(const wxString& str,
const wxString& domain = wxString(),
const wxString& context = wxString())
inline const wxString& wxGetTranslation(const wxString& str,
const wxString& domain = wxString(),
const wxString& context = wxString())
{
wxTranslations *trans = wxTranslations::Get();
const wxString *transStr = trans ? trans->GetTranslatedString(str, domain, context)
@@ -285,14 +284,16 @@ inline wxString wxGetTranslation(const wxString& str,
if ( transStr )
return *transStr;
else
return str;
// NB: this function returns reference to a string, so we have to keep
// a copy of it somewhere
return wxTranslations::GetUntranslatedString(str);
}
inline wxString wxGetTranslation(const wxString& str1,
const wxString& str2,
unsigned n,
const wxString& domain = wxString(),
const wxString& context = wxString())
inline const wxString& wxGetTranslation(const wxString& str1,
const wxString& str2,
unsigned n,
const wxString& domain = wxString(),
const wxString& context = wxString())
{
wxTranslations *trans = wxTranslations::Get();
const wxString *transStr = trans ? trans->GetTranslatedString(str1, n, domain, context)
@@ -300,7 +301,11 @@ inline wxString wxGetTranslation(const wxString& str1,
if ( transStr )
return *transStr;
else
return n == 1 ? str1 : str2;
// NB: this function returns reference to a string, so we have to keep
// a copy of it somewhere
return n == 1
? wxTranslations::GetUntranslatedString(str1)
: wxTranslations::GetUntranslatedString(str2);
}
#ifdef wxNO_IMPLICIT_WXSTRING_ENCODING
@@ -309,20 +314,19 @@ inline wxString wxGetTranslation(const wxString& str1,
* It must always be possible to call wxGetTranslation() with const
* char* arguments.
*/
inline wxString wxGetTranslation(const char *str,
const char *domain = "",
const char *context = "") {
inline const wxString& wxGetTranslation(const char *str,
const char *domain = "",
const char *context = "") {
const wxMBConv &conv = wxConvWhateverWorks;
return wxGetTranslation(wxString(str, conv), wxString(domain, conv),
wxString(context, conv));
}
inline wxString wxGetTranslation(const char *str1,
const char *str2,
unsigned n,
const char *domain = "",
const char *context = "") {
inline const wxString& wxGetTranslation(const char *str1,
const char *str2,
unsigned n,
const char *domain = "",
const char *context = "") {
const wxMBConv &conv = wxConvWhateverWorks;
return wxGetTranslation(wxString(str1, conv), wxString(str2, conv), n,
wxString(domain, conv),
@@ -379,7 +383,7 @@ inline wxString wxTRANS_INPUT_STR(const wchar_t* s)
*/
template<size_t N, typename T>
wxString wxUnderscoreWrapper(const T (&msg)[N])
const wxString& wxUnderscoreWrapper(const T (&msg)[N])
{
return wxGetTranslation(wxTRANS_INPUT_STR(msg));
}
@@ -392,9 +396,9 @@ wxString wxUnderscoreWrapper(T)
}
template<size_t M, size_t N, typename T>
wxString wxPluralWrapper(const T (&msg)[M],
const T (&plural)[N],
int count)
const wxString& wxPluralWrapper(const T (&msg)[M],
const T (&plural)[N],
int count)
{
return wxGetTranslation(wxTRANS_INPUT_STR(msg), wxTRANS_INPUT_STR(plural),
count);
@@ -408,8 +412,8 @@ wxString wxPluralWrapper(T, U, int)
}
template<size_t M, size_t N, typename T>
wxString wxGettextInContextWrapper(const T (&ctx)[M],
const T (&msg)[N])
const wxString& wxGettextInContextWrapper(const T (&ctx)[M],
const T (&msg)[N])
{
return wxGetTranslation(wxTRANS_INPUT_STR(msg), wxString(),
wxTRANS_INPUT_STR(ctx));
@@ -423,10 +427,10 @@ wxString wxGettextInContextWrapper(T, U)
}
template<size_t L, size_t M, size_t N, typename T>
wxString wxGettextInContextPluralWrapper(const T (&ctx)[L],
const T (&msg)[M],
const T (&plural)[N],
int count)
const wxString& wxGettextInContextPluralWrapper(const T (&ctx)[L],
const T (&msg)[M],
const T (&plural)[N],
int count)
{
return wxGetTranslation(wxTRANS_INPUT_STR(msg), wxTRANS_INPUT_STR(plural),
count, wxString(), wxTRANS_INPUT_STR(ctx));
@@ -443,30 +447,30 @@ wxString wxGettextInContextPluralWrapper(T, U, V, int)
// Wrapper functions that accept both string literals and variables
// as arguments.
inline wxString wxUnderscoreWrapper(const char *msg)
inline const wxString& wxUnderscoreWrapper(const char *msg)
{
return wxGetTranslation(wxTRANS_INPUT_STR(msg));
}
inline wxString wxPluralWrapper(const char *msg,
const char *plural,
int count)
inline const wxString& wxPluralWrapper(const char *msg,
const char *plural,
int count)
{
return wxGetTranslation(wxTRANS_INPUT_STR(msg), wxTRANS_INPUT_STR(plural),
count);
}
inline wxString wxGettextInContextWrapper(const char *ctx,
const char *msg)
inline const wxString& wxGettextInContextWrapper(const char *ctx,
const char *msg)
{
return wxGetTranslation(wxTRANS_INPUT_STR(msg), wxString(),
wxTRANS_INPUT_STR(ctx));
}
inline wxString wxGettextInContextPluralWrapper(const char *ctx,
const char *msg,
const char *plural,
int count)
inline const wxString& wxGettextInContextPluralWrapper(const char *ctx,
const char *msg,
const char *plural,
int count)
{
return wxGetTranslation(wxTRANS_INPUT_STR(msg), wxTRANS_INPUT_STR(plural),
count, wxString(), wxTRANS_INPUT_STR(ctx));
+5 -5
View File
@@ -494,15 +494,15 @@ public:
/**
Calls wxGetTranslation(const wxString&, const wxString&).
*/
wxString GetString(const wxString& origString,
const wxString& domain = wxEmptyString) const;
const wxString& GetString(const wxString& origString,
const wxString& domain = wxEmptyString) const;
/**
Calls wxGetTranslation(const wxString&, const wxString&, unsigned, const wxString&).
*/
wxString GetString(const wxString& origString,
const wxString& origString2, unsigned n,
const wxString& domain = wxEmptyString) const;
const wxString& GetString(const wxString& origString,
const wxString& origString2, unsigned n,
const wxString& domain = wxEmptyString) const;
/**
Returns current platform-specific locale name as passed to setlocale().
+7 -7
View File
@@ -612,9 +612,9 @@ public:
@header{wx/intl.h}
*/
wxString wxGetTranslation(const wxString& string,
const wxString& domain = wxEmptyString,
const wxString& context = wxEmptyString);
const wxString& wxGetTranslation(const wxString& string,
const wxString& domain = wxEmptyString,
const wxString& context = wxEmptyString);
/**
This is an overloaded version of
@@ -638,10 +638,10 @@ wxString wxGetTranslation(const wxString& string,
@header{wx/intl.h}
*/
wxString wxGetTranslation(const wxString& string,
const wxString& plural, unsigned n,
const wxString& domain = wxEmptyString,
const wxString& context = wxEmptyString);
const wxString& wxGetTranslation(const wxString& string,
const wxString& plural, unsigned n,
const wxString& domain = wxEmptyString,
const wxString& context = wxEmptyString);
/**
Macro to be used around all literal strings that should be translated.
+96
View File
@@ -53,7 +53,12 @@
#include "wx/msw/missing.h"
#endif
#ifdef __MINGW32__
#include <map>
#endif
#include <memory>
#include <unordered_set>
// ----------------------------------------------------------------------------
// simple types
@@ -1450,6 +1455,97 @@ wxString wxTranslations::DoGetBestAvailableTranslation(const wxString& domain, c
return lang;
}
namespace
{
// We use this container to store all strings known not to have translations.
// It is thread-specific to avoid using mutexes for every untranslated string
// access.
using UntranslatedStrings = std::unordered_set<wxString>;
/*
As of October 2025, MinGW still has a long-standing bug in its thread_local
variables implementation: their memory is de-allocated *before* their
destructor is called, see https://github.com/msys2/MINGW-packages/issues/2519
The UntranslatedStringHolder class works around this issue, by storing data
in global variables outside of this class and only relying on the dtor to
be executed when any thread (not necessarily created by wxWidgets) exits to
ensure that we always perform the required cleanup.
*/
#ifdef __MINGW32__
class UntranslatedStringHolder
{
private:
static wxCriticalSection ms_criticalSection;
static std::map<wxThreadIdType, UntranslatedStrings> ms_setsMap;
// This will be set to point to an element of ms_setsMap.
UntranslatedStrings* m_holder = nullptr;
public:
UntranslatedStringHolder() = default;
const wxString& get(const wxString& str)
{
if ( m_holder == nullptr )
{
wxCriticalSectionLocker locker(ms_criticalSection);
m_holder = &ms_setsMap[wxThread::GetCurrentId()];
}
return *m_holder->insert(str).first;
}
~UntranslatedStringHolder()
{
// This code is run after this object memory has been deallocated so we
// cannot access any member variables, but we can access global ones.
wxCriticalSectionLocker locker(ms_criticalSection);
ms_setsMap.erase(wxThread::GetCurrentId());
}
wxDECLARE_NO_COPY_CLASS(UntranslatedStringHolder);
};
wxCriticalSection UntranslatedStringHolder::ms_criticalSection;
std::map<wxThreadIdType, UntranslatedStrings> UntranslatedStringHolder::ms_setsMap;
#else // !__MINGW32__
// When not using MinGW, thread_local variables to work correctly but we still
// define this class, even if it's trivial, to use the same code below.
class UntranslatedStringHolder
{
private:
UntranslatedStrings m_holder;
public:
UntranslatedStringHolder() = default;
const wxString& get(const wxString& str)
{
return *m_holder.insert(str).first;
}
wxDECLARE_NO_COPY_CLASS(UntranslatedStringHolder);
};
#endif // __MINGW32__/!__MINGW32__
} // Anonymous namespace
/* static */
const wxString& wxTranslations::GetUntranslatedString(const wxString& str)
{
thread_local UntranslatedStringHolder wxPerThreadStrings;
return wxPerThreadStrings.get(str);
}
const wxString *wxTranslations::GetTranslatedString(const wxString& origString,
const wxString& domain,
const wxString& context) const