mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Chat: Chat translation improvements
* Remove hyperlinks from translated chat messages * Implement case preservation rules depending on receiver client locale
This commit is contained in:
@@ -291,6 +291,24 @@ inline wchar_t wcharToLower(wchar_t wchar)
|
||||
return wchar;
|
||||
}
|
||||
|
||||
inline bool isLower(wchar_t wchar)
|
||||
{
|
||||
if (wchar >= L'a' && wchar <= L'z') // LATIN CAPITAL LETTER A - LATIN CAPITAL LETTER Z
|
||||
return true;
|
||||
if (wchar >= 0x00E0 && wchar <= 0x00FF) // LATIN SMALL LETTER A WITH GRAVE - LATIN SMALL LETTER Y WITH DIAERESIS
|
||||
return true;
|
||||
if (wchar >= 0x0430 && wchar <= 0x044F) // CYRILLIC SMALL LETTER A - CYRILLIC SMALL LETTER YA
|
||||
return true;
|
||||
if (wchar == 0x0451) // CYRILLIC SMALL LETTER IO
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isUpper(wchar_t wchar)
|
||||
{
|
||||
return !isLower(wchar);
|
||||
}
|
||||
|
||||
TC_COMMON_API std::wstring wstrCaseAccentInsensitiveParse(std::wstring const& wstr, LocaleConstant locale);
|
||||
|
||||
TC_COMMON_API void wstrToUpper(std::wstring& str);
|
||||
|
||||
@@ -106,60 +106,169 @@ LanguageMgr::WordList const* LanguageMgr::FindWordGroup(uint32 language, uint32
|
||||
return Trinity::Containers::MapGetValuePtr(_wordsMap, WordKey(language, wordLen));
|
||||
}
|
||||
|
||||
std::string LanguageMgr::Translate(std::string const& msg, uint32 sourcePlayerLanguage) const
|
||||
namespace
|
||||
{
|
||||
std::stringstream result;
|
||||
Tokenizer tokens(msg, ' ');
|
||||
bool first = true;
|
||||
void StripHyperlinks(std::string const& source, std::string& dest)
|
||||
{
|
||||
dest.resize(source.length());
|
||||
size_t destSize = 0;
|
||||
bool skipSquareBrackets = false;
|
||||
for (size_t i = 0; i < source.length(); ++i)
|
||||
{
|
||||
char c = source[i];
|
||||
if (c != '|')
|
||||
{
|
||||
if (!skipSquareBrackets || (c != '[' && c != ']'))
|
||||
dest[destSize++] = source[i];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 1 >= source.length())
|
||||
break;
|
||||
|
||||
switch (source[i + 1])
|
||||
{
|
||||
case 'c':
|
||||
case 'C':
|
||||
// skip color
|
||||
i += 9;
|
||||
break;
|
||||
case 'r':
|
||||
++i;
|
||||
break;
|
||||
case 'H':
|
||||
// skip just past first |h
|
||||
i = source.find("|h", i);
|
||||
if (i != std::string::npos)
|
||||
i += 2;
|
||||
skipSquareBrackets = true;
|
||||
break;
|
||||
case 'h':
|
||||
++i;
|
||||
skipSquareBrackets = false;
|
||||
break;
|
||||
case 'T':
|
||||
// skip just past closing |t
|
||||
i = source.find("|t", i);
|
||||
if (i != std::string::npos)
|
||||
i += 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dest.resize(destSize);
|
||||
}
|
||||
|
||||
void ReplaceUntranslatableCharactersWithSpace(std::string& text)
|
||||
{
|
||||
std::wstring wstrText;
|
||||
if (!Utf8toWStr(text, wstrText))
|
||||
return;
|
||||
|
||||
for (wchar_t& w : wstrText)
|
||||
if (!isExtendedLatinCharacter(w) && !isNumeric(w) && w <= 0xFF && w != L'\\')
|
||||
w = L' ';
|
||||
|
||||
WStrToUtf8(wstrText, text);
|
||||
}
|
||||
|
||||
static char upper_backslash(char c)
|
||||
{
|
||||
return c == '/' ? '\\' : char(toupper(c));
|
||||
}
|
||||
|
||||
static uint32 const sstr_hashtable[16] =
|
||||
{
|
||||
0x486E26EE, 0xDCAA16B3, 0xE1918EEF, 0x202DAFDB,
|
||||
0x341C7DC7, 0x1C365303, 0x40EF2D37, 0x65FD5E49,
|
||||
0xD6057177, 0x904ECE93, 0x1C38024F, 0x98FD323B,
|
||||
0xE3061AE7, 0xA39B0FA1, 0x9797F25F, 0xE4444563,
|
||||
};
|
||||
|
||||
uint32 SStrHash(char const* string, bool caseInsensitive, uint32 seed = 0x7FED7FED)
|
||||
{
|
||||
ASSERT(string);
|
||||
|
||||
uint32 shift = 0xEEEEEEEE;
|
||||
while (*string)
|
||||
{
|
||||
char c = *string++;
|
||||
|
||||
if (caseInsensitive)
|
||||
c = upper_backslash(c);
|
||||
|
||||
seed = (sstr_hashtable[c >> 4] - sstr_hashtable[c & 0xF]) ^ (shift + seed);
|
||||
shift = c + seed + 33 * shift + 3;
|
||||
}
|
||||
|
||||
return seed ? seed : 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::string LanguageMgr::Translate(std::string const& msg, uint32 language, LocaleConstant locale) const
|
||||
{
|
||||
std::string textToTranslate;
|
||||
StripHyperlinks(msg, textToTranslate);
|
||||
ReplaceUntranslatableCharactersWithSpace(textToTranslate);
|
||||
|
||||
std::string result;
|
||||
result.reserve(textToTranslate.length());
|
||||
Tokenizer tokens(textToTranslate, ' ');
|
||||
for (char const* str : tokens)
|
||||
{
|
||||
const char* nextPart = str;
|
||||
uint32 wordLen = std::min(18U, (uint32)strlen(str));
|
||||
LanguageMgr::WordList const* wordGroup = FindWordGroup(sourcePlayerLanguage, wordLen);
|
||||
if (!wordGroup)
|
||||
nextPart = "";
|
||||
else
|
||||
uint32 wordLen = std::min(18u, uint32(strlen(str)));
|
||||
if (LanguageMgr::WordList const* wordGroup = FindWordGroup(language, wordLen))
|
||||
{
|
||||
uint32 wordHash = SStrHash(str, true);
|
||||
uint8 idxInsideGroup = wordHash % wordGroup->size();
|
||||
nextPart = wordGroup->at(idxInsideGroup);
|
||||
|
||||
char const* replacementWord = (*wordGroup)[idxInsideGroup];
|
||||
|
||||
switch (locale)
|
||||
{
|
||||
case LOCALE_koKR:
|
||||
case LOCALE_zhCN:
|
||||
case LOCALE_zhTW:
|
||||
{
|
||||
size_t length = std::min(strlen(str), strlen(replacementWord));
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
{
|
||||
if (str[i] >= 'A' && str[i] <= 'Z')
|
||||
result += char(toupper(replacementWord[i]));
|
||||
else
|
||||
result += replacementWord[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::wstring wstrSourceWord;
|
||||
if (Utf8toWStr(str, wstrSourceWord))
|
||||
{
|
||||
size_t length = std::min(wstrSourceWord.length(), strlen(replacementWord));
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
{
|
||||
if (isUpper(wstrSourceWord[i]))
|
||||
result += char(toupper(replacementWord[i]));
|
||||
else
|
||||
result += char(tolower(replacementWord[i]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
result << " ";
|
||||
|
||||
result << nextPart;
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
|
||||
static char upper_backslash(char c) { return c == '/' ? '\\' : toupper(c); }
|
||||
|
||||
static uint32 const s_hashtable[16] = {
|
||||
0x486E26EE, 0xDCAA16B3, 0xE1918EEF, 0x202DAFDB,
|
||||
0x341C7DC7, 0x1C365303, 0x40EF2D37, 0x65FD5E49,
|
||||
0xD6057177, 0x904ECE93, 0x1C38024F, 0x98FD323B,
|
||||
0xE3061AE7, 0xA39B0FA1, 0x9797F25F, 0xE4444563,
|
||||
};
|
||||
|
||||
uint32 LanguageMgr::SStrHash(char const* string, bool caseInsensitive, uint32 seed) const
|
||||
{
|
||||
ASSERT(string);
|
||||
|
||||
uint32 shift = 0xEEEEEEEE;
|
||||
while (*string) {
|
||||
char c = *string++;
|
||||
|
||||
if (caseInsensitive)
|
||||
c = upper_backslash(c);
|
||||
|
||||
seed = (s_hashtable[c >> 4] - s_hashtable[c & 0xF]) ^ (shift + seed);
|
||||
shift = c + seed + 33 * shift + 3;
|
||||
result += ' ';
|
||||
}
|
||||
|
||||
return seed ? seed : 1;
|
||||
if (!result.empty())
|
||||
result.pop_back();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LanguageMgr::IsLanguageExist(uint32 languageId) const
|
||||
|
||||
@@ -18,11 +18,10 @@
|
||||
#ifndef _LANGUAGE_MGR_H
|
||||
#define _LANGUAGE_MGR_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "Common.h"
|
||||
#include "Hash.h"
|
||||
#include "IteratorPair.h"
|
||||
#include "SharedDefines.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -60,9 +59,7 @@ class TC_GAME_API LanguageMgr
|
||||
static LanguageMgr* instance();
|
||||
|
||||
//
|
||||
std::string Translate(std::string const& msg, uint32 sourcePlayerLanguage) const;
|
||||
|
||||
uint32 SStrHash(char const* string, bool caseInsensitive, uint32 seed = 0x7FED7FED) const;
|
||||
std::string Translate(std::string const& msg, uint32 language, LocaleConstant locale) const;
|
||||
|
||||
bool IsLanguageExist(uint32 languageId) const;
|
||||
Trinity::IteratorPair<LanguagesMap::const_iterator> GetLanguageDescById(Language languageId) const;
|
||||
|
||||
@@ -21972,13 +21972,14 @@ void Player::Say(std::string const& text, Language language, WorldObject const*
|
||||
|
||||
void Player::SendChatMessageToSetInRange(ChatMsg chatMsg, Language language, std::string&& text, float range)
|
||||
{
|
||||
Trinity::ChatPacketSender sender(chatMsg, language, this, this, std::move(text));
|
||||
Trinity::CustomChatTextBuilder builder(this, chatMsg, std::move(text), language, this);
|
||||
Trinity::LocalizedDo<Trinity::CustomChatTextBuilder> localizer(builder);
|
||||
|
||||
// Send to self
|
||||
sender(this);
|
||||
localizer(this);
|
||||
|
||||
// Send to players
|
||||
Trinity::MessageDistDeliverer<Trinity::ChatPacketSender> notifier(this, sender, range);
|
||||
Trinity::MessageDistDeliverer<Trinity::LocalizedDo<Trinity::CustomChatTextBuilder>> notifier(this, localizer, range);
|
||||
Cell::VisitWorldObjects(this, notifier, range);
|
||||
}
|
||||
|
||||
|
||||
@@ -1612,7 +1612,7 @@ namespace Trinity
|
||||
public:
|
||||
explicit LocalizedDo(Localizer& localizer) : _localizer(localizer) { }
|
||||
|
||||
void operator()(Player* p);
|
||||
void operator()(Player const* p);
|
||||
|
||||
private:
|
||||
Localizer& _localizer;
|
||||
|
||||
@@ -750,7 +750,7 @@ void Trinity::PlayerLastSearcher<Check>::Visit(PlayerMapType& m)
|
||||
}
|
||||
|
||||
template<typename Localizer>
|
||||
void Trinity::LocalizedDo<Localizer>::operator()(Player* p)
|
||||
void Trinity::LocalizedDo<Localizer>::operator()(Player const* p)
|
||||
{
|
||||
LocaleConstant loc_idx = p->GetSession()->GetSessionDbLocaleIndex();
|
||||
uint32 cache_idx = loc_idx + 1;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "LanguageMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "WorldSession.h"
|
||||
#include <cstdarg>
|
||||
|
||||
namespace Trinity
|
||||
@@ -44,7 +45,8 @@ void ChatPacketSender::operator()(Player const* player) const
|
||||
if (!TranslatedPacket)
|
||||
{
|
||||
TranslatedPacket.emplace();
|
||||
TranslatedPacket->Initialize(Type, Language, Sender, Receiver, sLanguageMgr->Translate(Text, Language), AchievementId, "", Locale);
|
||||
TranslatedPacket->Initialize(Type, Language, Sender, Receiver, sLanguageMgr->Translate(Text, Language, player->GetSession()->GetSessionDbcLocale()),
|
||||
AchievementId, "", Locale);
|
||||
TranslatedPacket->Write();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user