mirror of https://github.com/PCSX2/pcsx2.git
Host: Add message translation functions
This commit is contained in:
parent
ff02d41992
commit
f7bc05c735
|
@ -658,6 +658,18 @@ void Host::VSyncOnCPUThread()
|
||||||
GSRunner::PumpPlatformMessages();
|
GSRunner::PumpPlatformMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32 Host::Internal::GetTranslatedStringImpl(
|
||||||
|
const std::string_view& context, const std::string_view& msg, char* tbuf, size_t tbuf_space)
|
||||||
|
{
|
||||||
|
if (msg.size() > tbuf_space)
|
||||||
|
return -1;
|
||||||
|
else if (msg.empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
std::memcpy(tbuf, msg.data(), msg.size());
|
||||||
|
return static_cast<s32>(msg.size());
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Platform specific code
|
// Platform specific code
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -1179,6 +1179,23 @@ void Host::SetFullscreen(bool enabled)
|
||||||
g_emu_thread->setFullscreen(enabled, true);
|
g_emu_thread->setFullscreen(enabled, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32 Host::Internal::GetTranslatedStringImpl(
|
||||||
|
const std::string_view& context, const std::string_view& msg, char* tbuf, size_t tbuf_space)
|
||||||
|
{
|
||||||
|
// This is really awful. Thankfully we're caching the results...
|
||||||
|
const std::string temp_context(context);
|
||||||
|
const std::string temp_msg(msg);
|
||||||
|
const QString translated_msg = qApp->translate(temp_context.c_str(), temp_msg.c_str());
|
||||||
|
const QByteArray translated_utf8 = translated_msg.toUtf8();
|
||||||
|
const size_t translated_size = translated_utf8.size();
|
||||||
|
if (translated_size > tbuf_space)
|
||||||
|
return -1;
|
||||||
|
else if (translated_size > 0)
|
||||||
|
std::memcpy(tbuf, translated_utf8.constData(), translated_size);
|
||||||
|
|
||||||
|
return static_cast<s32>(translated_size);
|
||||||
|
}
|
||||||
|
|
||||||
alignas(16) static SysMtgsThread s_mtgs_thread;
|
alignas(16) static SysMtgsThread s_mtgs_thread;
|
||||||
|
|
||||||
SysMtgsThread& GetMTGS()
|
SysMtgsThread& GetMTGS()
|
||||||
|
|
113
pcsx2/Host.cpp
113
pcsx2/Host.cpp
|
@ -26,13 +26,122 @@
|
||||||
#include "common/Assertions.h"
|
#include "common/Assertions.h"
|
||||||
#include "common/CrashHandler.h"
|
#include "common/CrashHandler.h"
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
|
#include "common/HeterogeneousContainers.h"
|
||||||
#include "common/Path.h"
|
#include "common/Path.h"
|
||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
static std::mutex s_settings_mutex;
|
namespace Host
|
||||||
static LayeredSettingsInterface s_layered_settings_interface;
|
{
|
||||||
|
static std::pair<const char*, u32> LookupTranslationString(
|
||||||
|
const std::string_view& context, const std::string_view& msg);
|
||||||
|
|
||||||
|
static std::mutex s_settings_mutex;
|
||||||
|
static LayeredSettingsInterface s_layered_settings_interface;
|
||||||
|
|
||||||
|
static constexpr u32 TRANSLATION_STRING_CACHE_SIZE = 4 * 1024 * 1024;
|
||||||
|
using TranslationStringMap = UnorderedStringMap<std::pair<u32, u32>>;
|
||||||
|
using TranslationStringContextMap = UnorderedStringMap<TranslationStringMap>;
|
||||||
|
static std::shared_mutex s_translation_string_mutex;
|
||||||
|
static TranslationStringContextMap s_translation_string_map;
|
||||||
|
static std::vector<char> s_translation_string_cache;
|
||||||
|
static u32 s_translation_string_cache_pos;
|
||||||
|
} // namespace Host
|
||||||
|
|
||||||
|
std::pair<const char*, u32> Host::LookupTranslationString(const std::string_view& context, const std::string_view& msg)
|
||||||
|
{
|
||||||
|
// TODO: TranslatableString, compile-time hashing.
|
||||||
|
|
||||||
|
TranslationStringContextMap::iterator ctx_it;
|
||||||
|
TranslationStringMap::iterator msg_it;
|
||||||
|
std::pair<const char*, u32> ret;
|
||||||
|
s32 len;
|
||||||
|
|
||||||
|
// Shouldn't happen, but just in case someone tries to translate an empty string.
|
||||||
|
if (unlikely(msg.empty()))
|
||||||
|
{
|
||||||
|
ret.first = &s_translation_string_cache[0];
|
||||||
|
ret.second = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_translation_string_mutex.lock_shared();
|
||||||
|
ctx_it = UnorderedStringMapFind(s_translation_string_map, context);
|
||||||
|
|
||||||
|
if (unlikely(ctx_it == s_translation_string_map.end()))
|
||||||
|
goto add_string;
|
||||||
|
|
||||||
|
msg_it = UnorderedStringMapFind(ctx_it->second, msg);
|
||||||
|
if (unlikely(msg_it == ctx_it->second.end()))
|
||||||
|
goto add_string;
|
||||||
|
|
||||||
|
ret.first = &s_translation_string_cache[msg_it->second.first];
|
||||||
|
ret.second = msg_it->second.second;
|
||||||
|
s_translation_string_mutex.unlock_shared();
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
add_string:
|
||||||
|
s_translation_string_mutex.unlock_shared();
|
||||||
|
s_translation_string_mutex.lock();
|
||||||
|
|
||||||
|
if (unlikely(s_translation_string_cache.empty()))
|
||||||
|
{
|
||||||
|
// First element is always an empty string.
|
||||||
|
s_translation_string_cache.resize(TRANSLATION_STRING_CACHE_SIZE);
|
||||||
|
s_translation_string_cache[0] = '\0';
|
||||||
|
s_translation_string_cache_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((len = Internal::GetTranslatedStringImpl(context, msg,
|
||||||
|
&s_translation_string_cache[s_translation_string_cache_pos],
|
||||||
|
TRANSLATION_STRING_CACHE_SIZE - 1 - s_translation_string_cache_pos)) < 0)
|
||||||
|
{
|
||||||
|
Console.Error("WARNING: Clearing translation string cache, it might need to be larger.");
|
||||||
|
s_translation_string_cache_pos = 0;
|
||||||
|
if ((len = Internal::GetTranslatedStringImpl(context, msg,
|
||||||
|
&s_translation_string_cache[s_translation_string_cache_pos],
|
||||||
|
TRANSLATION_STRING_CACHE_SIZE - 1 - s_translation_string_cache_pos)) < 0)
|
||||||
|
{
|
||||||
|
pxFailRel("Failed to get translated string after clearing cache.");
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New context?
|
||||||
|
if (ctx_it == s_translation_string_map.end())
|
||||||
|
ctx_it = s_translation_string_map.emplace(context, TranslationStringMap()).first;
|
||||||
|
|
||||||
|
// Impl doesn't null terminate, we need that for C strings.
|
||||||
|
// TODO: do we want to consider aligning the buffer?
|
||||||
|
const u32 insert_pos = s_translation_string_cache_pos;
|
||||||
|
s_translation_string_cache[insert_pos + static_cast<u32>(len)] = 0;
|
||||||
|
|
||||||
|
ctx_it->second.emplace(msg, std::pair<u32, u32>(insert_pos, static_cast<u32>(len)));
|
||||||
|
s_translation_string_cache_pos = insert_pos + static_cast<u32>(len) + 1;
|
||||||
|
|
||||||
|
ret.first = &s_translation_string_cache[insert_pos];
|
||||||
|
ret.second = static_cast<u32>(len);
|
||||||
|
s_translation_string_mutex.unlock();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Host::TranslateToCString(const std::string_view& context, const std::string_view& msg)
|
||||||
|
{
|
||||||
|
return LookupTranslationString(context, msg).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view Host::TranslateToStringView(const std::string_view& context, const std::string_view& msg)
|
||||||
|
{
|
||||||
|
const auto mp = LookupTranslationString(context, msg);
|
||||||
|
return std::string_view(mp.first, mp.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Host::TranslateToString(const std::string_view& context, const std::string_view& msg)
|
||||||
|
{
|
||||||
|
return std::string(TranslateToStringView(context, msg));
|
||||||
|
}
|
||||||
|
|
||||||
void Host::ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...)
|
void Host::ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...)
|
||||||
{
|
{
|
||||||
|
|
31
pcsx2/Host.h
31
pcsx2/Host.h
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include "common/Pcsx2Defs.h"
|
#include "common/Pcsx2Defs.h"
|
||||||
|
|
||||||
|
#include "fmt/format.h"
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -48,6 +50,24 @@ namespace Host
|
||||||
/// Returns the modified time of a resource.
|
/// Returns the modified time of a resource.
|
||||||
std::optional<std::time_t> GetResourceFileTimestamp(const char* filename);
|
std::optional<std::time_t> GetResourceFileTimestamp(const char* filename);
|
||||||
|
|
||||||
|
/// Returns a localized version of the specified string within the specified context.
|
||||||
|
/// The pointer is guaranteed to be valid until the next language change.
|
||||||
|
const char* TranslateToCString(const std::string_view& context, const std::string_view& msg);
|
||||||
|
|
||||||
|
/// Returns a localized version of the specified string within the specified context.
|
||||||
|
/// The view is guaranteed to be valid until the next language change.
|
||||||
|
/// NOTE: When passing this to fmt, positional arguments should be used in the base string, as
|
||||||
|
/// not all locales follow the same word ordering.
|
||||||
|
std::string_view TranslateToStringView(const std::string_view& context, const std::string_view& msg);
|
||||||
|
|
||||||
|
/// Returns a localized version of the specified string within the specified context.
|
||||||
|
std::string TranslateToString(const std::string_view& context, const std::string_view& msg);
|
||||||
|
|
||||||
|
/// Returns a localized version of the specified string, after formatting.
|
||||||
|
|
||||||
|
template<typename... T>
|
||||||
|
std::string TranslateAndFmt(const std::string_view& context, const std::string_view& format, T&&... args);
|
||||||
|
|
||||||
/// Adds OSD messages, duration is in seconds.
|
/// Adds OSD messages, duration is in seconds.
|
||||||
void AddOSDMessage(std::string message, float duration = 2.0f);
|
void AddOSDMessage(std::string message, float duration = 2.0f);
|
||||||
void AddKeyedOSDMessage(std::string key, std::string message, float duration = 2.0f);
|
void AddKeyedOSDMessage(std::string key, std::string message, float duration = 2.0f);
|
||||||
|
@ -155,5 +175,16 @@ namespace Host
|
||||||
|
|
||||||
/// Sets the input profile settings layer. Called by VMManager when the game changes.
|
/// Sets the input profile settings layer. Called by VMManager when the game changes.
|
||||||
void SetInputSettingsLayer(SettingsInterface* sif);
|
void SetInputSettingsLayer(SettingsInterface* sif);
|
||||||
|
|
||||||
|
/// Implementation to retrieve a translated string.
|
||||||
|
s32 GetTranslatedStringImpl(const std::string_view& context, const std::string_view& msg, char* tbuf, size_t tbuf_space);
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Host
|
} // namespace Host
|
||||||
|
|
||||||
|
// Helper macros for retrieving translated strings.
|
||||||
|
#define TRANSLATE(context, msg) Host::TranslateToCString(context, msg)
|
||||||
|
#define TRANSLATE_SV(context, msg) Host::TranslateToStringView(context, msg)
|
||||||
|
#define TRANSLATE_STR(context, msg) Host::TranslateToString(context, msg)
|
||||||
|
|
||||||
|
// Does not translate the string at runtime, but allows the UI to in its own way.
|
||||||
|
#define TRANSLATE_NOOP(context, msg) msg
|
||||||
|
|
Loading…
Reference in New Issue