From c665330843187b5cbeb8c29fb029b0c0fa3e4e9a Mon Sep 17 00:00:00 2001 From: hondacrx Date: Tue, 4 May 2021 10:45:19 -0400 Subject: [PATCH] Core/Chat: Chat translation improvements (needs testing unsure) Port From (https://github.com/TrinityCore/TrinityCore/commit/67e16888ac76d144fab7433b542d660b13e7cb62) --- Source/Framework/Util/Extensions.cs | 30 +++++ Source/Game/Chat/LanguageManager.cs | 176 ++++++++++++++++++++------ Source/Game/Entities/Player/Player.cs | 7 +- Source/Game/Text/ChatTextBuilder.cs | 2 +- 4 files changed, 173 insertions(+), 42 deletions(-) diff --git a/Source/Framework/Util/Extensions.cs b/Source/Framework/Util/Extensions.cs index 70e145a68..19d8f2e9b 100644 --- a/Source/Framework/Util/Extensions.cs +++ b/Source/Framework/Util/Extensions.cs @@ -225,6 +225,36 @@ namespace System return Encoding.UTF8.GetByteCount(str); } + + public static bool isExtendedLatinCharacter(char wchar) + { + if (isBasicLatinCharacter(wchar)) + return true; + if (wchar >= 0x00C0 && wchar <= 0x00D6) // LATIN CAPITAL LETTER A WITH GRAVE - LATIN CAPITAL LETTER O WITH DIAERESIS + return true; + if (wchar >= 0x00D8 && wchar <= 0x00DE) // LATIN CAPITAL LETTER O WITH STROKE - LATIN CAPITAL LETTER THORN + return true; + if (wchar == 0x00DF) // LATIN SMALL LETTER SHARP S + return true; + if (wchar >= 0x00E0 && wchar <= 0x00F6) // LATIN SMALL LETTER A WITH GRAVE - LATIN SMALL LETTER O WITH DIAERESIS + return true; + if (wchar >= 0x00F8 && wchar <= 0x00FE) // LATIN SMALL LETTER O WITH STROKE - LATIN SMALL LETTER THORN + return true; + if (wchar >= 0x0100 && wchar <= 0x012F) // LATIN CAPITAL LETTER A WITH MACRON - LATIN SMALL LETTER I WITH OGONEK + return true; + if (wchar == 0x1E9E) // LATIN CAPITAL LETTER SHARP S + return true; + return false; + } + + public static bool isBasicLatinCharacter(char wchar) + { + if (wchar >= 'a' && wchar <= 'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z + return true; + if (wchar >= 'A' && wchar <= 'Z') // LATIN CAPITAL LETTER A - LATIN CAPITAL LETTER Z + return true; + return false; + } #endregion #region BinaryReader diff --git a/Source/Game/Chat/LanguageManager.cs b/Source/Game/Chat/LanguageManager.cs index 54f57819c..174b95f80 100644 --- a/Source/Game/Chat/LanguageManager.cs +++ b/Source/Game/Chat/LanguageManager.cs @@ -103,37 +103,155 @@ namespace Game.Chat return _wordsMap.LookupByKey(Tuple.Create(language, wordLen)); } - public string Translate(string msg, uint sourcePlayerLanguage) + public string Translate(string msg, uint language, Locale locale) { - StringBuilder result = new StringBuilder(); - StringArray tokens = new(msg, ' '); + string textToTranslate = ""; + StripHyperlinks(msg, ref textToTranslate); + ReplaceUntranslatableCharactersWithSpace(ref textToTranslate); - bool first = true; + string result = ""; + StringArray tokens = new(textToTranslate, ' '); foreach (string str in tokens) { - string nextPart = str; - uint wordLen = (uint)Math.Min(18U, str.Length); - var wordGroup = FindWordGroup(sourcePlayerLanguage, wordLen); - if (wordGroup.Empty()) - nextPart = ""; - else + uint wordLen = (uint)Math.Min(18, str.Length); + var wordGroup = FindWordGroup(language, wordLen); + if (!wordGroup.Empty()) { uint wordHash = SStrHash(str, true); - byte idxInsideGroup = (byte)(wordHash % wordGroup.Count); - nextPart = wordGroup[idxInsideGroup]; + byte idxInsideGroup = (byte)(wordHash % wordGroup.Count()); + + string replacementWord = wordGroup[idxInsideGroup]; + + switch (locale) + { + case Locale.koKR: + case Locale.zhCN: + case Locale.zhTW: + { + int length = Math.Min(str.Length, replacementWord.Length); + for (int i = 0; i < length; ++i) + { + if (str[i] >= 'A' && str[i] <= 'Z') + result += char.ToUpper(replacementWord[i]); + else + result += replacementWord[i]; + } + break; + } + default: + { + int length = Math.Min(str.Length, replacementWord.Length); + for (int i = 0; i < length; ++i) + { + if (char.IsUpper(str[i])) + result += char.ToUpper(replacementWord[i]); + else + result += char.ToLower(replacementWord[i]); + } + break; + } + } } - if (first) - first = false; - else - result.Append(" "); - - result.Append(nextPart); + result += ' '; } - return result.ToString(); + + if (!result.IsEmpty()) + result.Remove(result.Length - 1); + + return result; } - static char upper_backslash(char c) { return c == '/' ? '\\' : char.ToUpper(c); } + public bool IsLanguageExist(Language languageId) + { + return CliDB.LanguagesStorage.HasRecord((uint)languageId); + } + + public List GetLanguageDescById(Language languageId) + { + return _langsMap.LookupByKey((uint)languageId); + } + + public bool ForEachLanguage(Func callback) + { + foreach (var pair in _langsMap) + if (!callback(pair.Key, pair.Value)) + return false; + return true; + } + + void StripHyperlinks(string source, ref string dest) + { + char[] destChar = new char[source.Length]; + + int destSize = 0; + bool skipSquareBrackets = false; + for (int i = 0; i < source.Length; ++i) + { + char c = source[i]; + if (c != '|') + { + if (!skipSquareBrackets || (c != '[' && c != ']')) + destChar[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.IndexOf("|h", i); + if (i != -1) + i += 2; + skipSquareBrackets = true; + break; + case 'h': + ++i; + skipSquareBrackets = false; + break; + case 'T': + // skip just past closing |t + i = source.IndexOf("|t", i); + if (i != -1) + i += 2; + break; + default: + break; + } + } + + dest = new string(destChar, 0, destSize); + } + + void ReplaceUntranslatableCharactersWithSpace(ref string text) + { + var chars = text.ToCharArray(); + for (var i = 0; i < text.Length; ++i) + { + var w = chars[i]; + if (!Extensions.isExtendedLatinCharacter(w) && !char.IsNumber(w) && w <= 0xFF && w != '\\') + chars[i] = ' '; + } + + text = new string(chars); + } + + static char upper_backslash(char c) + { + return c == '/' ? '\\' : char.ToUpper(c); + } static uint[] s_hashtable = { @@ -158,23 +276,5 @@ namespace Game.Chat return seed != 0 ? seed : 1; } - - public bool IsLanguageExist(Language languageId) - { - return CliDB.LanguagesStorage.HasRecord((uint)languageId); - } - - public List GetLanguageDescById(Language languageId) - { - return _langsMap.LookupByKey((uint)languageId); - } - - public bool ForEachLanguage(Func callback) - { - foreach (var pair in _langsMap) - if (!callback(pair.Key, pair.Value)) - return false; - return true; - } } } diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 4d491ee69..032373bfa 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -6233,13 +6233,14 @@ namespace Game.Entities void SendChatMessageToSetInRange(ChatMsg chatMsg, Language language, string text, float range) { - ChatPacketSender sender = new(chatMsg, language, this, this, text); + CustomChatTextBuilder builder = new(this, chatMsg, text, language, this); + LocalizedDo localizer = new(builder); // Send to self - sender.Invoke(this); + localizer.Invoke(this); // Send to players - MessageDistDeliverer notifier = new(this, sender, range); + MessageDistDeliverer notifier = new(this, localizer, range); Cell.VisitWorldObjects(this, notifier, range); } diff --git a/Source/Game/Text/ChatTextBuilder.cs b/Source/Game/Text/ChatTextBuilder.cs index 8a4d913fe..3a9523fe8 100644 --- a/Source/Game/Text/ChatTextBuilder.cs +++ b/Source/Game/Text/ChatTextBuilder.cs @@ -69,7 +69,7 @@ namespace Game.Chat if (!TranslatedPacket.HasValue) { TranslatedPacket.HasValue = true; - TranslatedPacket.Value.Initialize(Type, Language, Sender, Receiver, Global.LanguageMgr.Translate(Text, (uint)Language), AchievementId, "", Locale); + TranslatedPacket.Value.Initialize(Type, Language, Sender, Receiver, Global.LanguageMgr.Translate(Text, (uint)Language, player.GetSession().GetSessionDbcLocale()), AchievementId, "", Locale); TranslatedPacket.Value.Write(); }