diff options
author | Shauren <shauren.trinity@gmail.com> | 2021-05-03 20:09:39 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2021-05-03 23:53:24 +0200 |
commit | 67e16888ac76d144fab7433b542d660b13e7cb62 (patch) | |
tree | a79271b1fcc2f56eec88f02357c95d3cdee2a3a2 | |
parent | 14098b28b39bc9d1ea17d18a7ecd3dd610f29cdc (diff) |
Core/Chat: Chat translation improvements
* Remove hyperlinks from translated chat messages
* Implement case preservation rules depending on receiver client locale
-rw-r--r-- | src/common/Utilities/Util.h | 18 | ||||
-rw-r--r-- | src/server/game/Chat/LanguageMgr.cpp | 185 | ||||
-rw-r--r-- | src/server/game/Chat/LanguageMgr.h | 7 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 7 | ||||
-rw-r--r-- | src/server/game/Grids/Notifiers/GridNotifiers.h | 2 | ||||
-rw-r--r-- | src/server/game/Grids/Notifiers/GridNotifiersImpl.h | 2 | ||||
-rw-r--r-- | src/server/game/Texts/ChatTextBuilder.cpp | 4 |
7 files changed, 176 insertions, 49 deletions
diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index 1060f5d090f..78672c22b12 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -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); diff --git a/src/server/game/Chat/LanguageMgr.cpp b/src/server/game/Chat/LanguageMgr.cpp index 5e81ed35193..ec2a65f9013 100644 --- a/src/server/game/Chat/LanguageMgr.cpp +++ b/src/server/game/Chat/LanguageMgr.cpp @@ -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; - for (char const* str : tokens) + void StripHyperlinks(std::string const& source, std::string& dest) { - const char* nextPart = str; - uint32 wordLen = std::min(18U, (uint32)strlen(str)); - LanguageMgr::WordList const* wordGroup = FindWordGroup(sourcePlayerLanguage, wordLen); - if (!wordGroup) - nextPart = ""; - else + dest.resize(source.length()); + size_t destSize = 0; + bool skipSquareBrackets = false; + for (size_t i = 0; i < source.length(); ++i) { - uint32 wordHash = SStrHash(str, true); - uint8 idxInsideGroup = wordHash % wordGroup->size(); - nextPart = wordGroup->at(idxInsideGroup); + 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; + } } - if (first) - first = false; - else - result << " "; + dest.resize(destSize); + } + + void ReplaceUntranslatableCharactersWithSpace(std::string& text) + { + std::wstring wstrText; + if (!Utf8toWStr(text, wstrText)) + return; - result << nextPart; + 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)); } - return result.str(); -} -static char upper_backslash(char c) { return c == '/' ? '\\' : 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++; -static uint32 const s_hashtable[16] = { - 0x486E26EE, 0xDCAA16B3, 0xE1918EEF, 0x202DAFDB, - 0x341C7DC7, 0x1C365303, 0x40EF2D37, 0x65FD5E49, - 0xD6057177, 0x904ECE93, 0x1C38024F, 0x98FD323B, - 0xE3061AE7, 0xA39B0FA1, 0x9797F25F, 0xE4444563, -}; + 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; + } +} -uint32 LanguageMgr::SStrHash(char const* string, bool caseInsensitive, uint32 seed) const +std::string LanguageMgr::Translate(std::string const& msg, uint32 language, LocaleConstant locale) const { - ASSERT(string); + std::string textToTranslate; + StripHyperlinks(msg, textToTranslate); + ReplaceUntranslatableCharactersWithSpace(textToTranslate); - uint32 shift = 0xEEEEEEEE; - while (*string) { - char c = *string++; + std::string result; + result.reserve(textToTranslate.length()); + Tokenizer tokens(textToTranslate, ' '); + for (char const* str : tokens) + { + 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(); - if (caseInsensitive) - c = upper_backslash(c); + char const* replacementWord = (*wordGroup)[idxInsideGroup]; - seed = (s_hashtable[c >> 4] - s_hashtable[c & 0xF]) ^ (shift + seed); - shift = c + seed + 33 * shift + 3; + 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; + } + } + } + + result += ' '; } - return seed ? seed : 1; + if (!result.empty()) + result.pop_back(); + + return result; } bool LanguageMgr::IsLanguageExist(uint32 languageId) const diff --git a/src/server/game/Chat/LanguageMgr.h b/src/server/game/Chat/LanguageMgr.h index 26010bfbcf8..7d1d4df123e 100644 --- a/src/server/game/Chat/LanguageMgr.h +++ b/src/server/game/Chat/LanguageMgr.h @@ -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; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index f2ceb6bfd1a..4f0b622f4b9 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -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); } diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 366abe3e8c5..6713698e1a2 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -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; diff --git a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h index 60c7f43a207..63daf6b6482 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h +++ b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h @@ -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; diff --git a/src/server/game/Texts/ChatTextBuilder.cpp b/src/server/game/Texts/ChatTextBuilder.cpp index ea564c2aa7f..2c3166775a5 100644 --- a/src/server/game/Texts/ChatTextBuilder.cpp +++ b/src/server/game/Texts/ChatTextBuilder.cpp @@ -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(); } |