diff --git a/Dalamud.Test/Dalamud.Test.csproj b/Dalamud.Test/Dalamud.Test.csproj
index 7091ebe26..1ae18862f 100644
--- a/Dalamud.Test/Dalamud.Test.csproj
+++ b/Dalamud.Test/Dalamud.Test.csproj
@@ -43,6 +43,7 @@
+
diff --git a/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs b/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs
new file mode 100644
index 000000000..b0ead50a2
--- /dev/null
+++ b/Dalamud.Test/Plugin/Sanitizer/SanitizerTests.cs
@@ -0,0 +1,67 @@
+// ReSharper disable StringLiteralTypo
+
+using System.Linq;
+using NUnit.Framework;
+
+namespace Dalamud.Test.Plugin.Sanitizer {
+ [TestFixture]
+ public class SanitizerTests {
+ global::Dalamud.Plugin.Sanitizer.Sanitizer sanitizer;
+
+ [Test]
+ public void Sanitize_NormalString_NoChange() {
+ sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.English);
+ const string str = "Pixie Cotton Hood of Healing";
+ var sanitizedString = sanitizer.Sanitize(str);
+ Assert.AreEqual(str, sanitizedString);
+ }
+
+ [Test]
+ public void Sanitize_DESpecialCharacters_Sanitized() {
+ sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German);
+ const string str = @"Anemos-Panzerhandschuhe des Drachenbluts";
+ var sanitizedString = sanitizer.Sanitize(str);
+ Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedString);
+ }
+
+ [Test]
+ public void Sanitize_FRSpecialCharacters_Sanitized() {
+ sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.French);
+ const string str = @"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse";
+ var sanitizedString = sanitizer.Sanitize(str);
+ Assert.AreEqual(@"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse", sanitizedString);
+ }
+
+ [Test]
+ public void Sanitize_SpecifyLanguage_Sanitized() {
+ sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.French);
+ const string str = @"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse";
+ var sanitizedString = sanitizer.Sanitize(str, ClientLanguage.French);
+ Assert.AreEqual(@"Le Diademe: terrains de chasse|Le Diademe: terrains de chasse", sanitizedString);
+ }
+
+ [Test]
+ public void Sanitize_List_Sanitized() {
+ sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German);
+ const string str = @"Anemos-Panzerhandschuhe des Drachenbluts";
+ var sanitizedStrings = sanitizer.Sanitize(new[] {str});
+ Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First());
+ }
+
+ [Test]
+ public void Sanitize_SpecifyLanguageList_Sanitized() {
+ sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.German);
+ const string str = @"Anemos-Panzerhandschuhe des Drachenbluts";
+ var sanitizedStrings = sanitizer.Sanitize(new[] {str}, ClientLanguage.German);
+ Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First());
+ }
+
+ [Test]
+ public void Sanitize_UseAlternateLanguage_Sanitized() {
+ sanitizer = new global::Dalamud.Plugin.Sanitizer.Sanitizer(ClientLanguage.English);
+ const string str = @"Anemos-Panzerhandschuhe des Drachenbluts";
+ var sanitizedStrings = sanitizer.Sanitize(new[] {str}, ClientLanguage.German);
+ Assert.AreEqual(@"Anemos-Panzerhandschuhe des Drachenbluts", sanitizedStrings.First());
+ }
+ }
+}
diff --git a/Dalamud.sln.DotSettings b/Dalamud.sln.DotSettings
index 8b8890e01..af8f4d833 100644
--- a/Dalamud.sln.DotSettings
+++ b/Dalamud.sln.DotSettings
@@ -47,4 +47,5 @@
True
True
True
- True
\ No newline at end of file
+ True
+ True
\ No newline at end of file
diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs
index c0d9f12e3..dab5995e7 100644
--- a/Dalamud/Plugin/DalamudPluginInterface.cs
+++ b/Dalamud/Plugin/DalamudPluginInterface.cs
@@ -13,6 +13,7 @@ using Dalamud.Game.ClientState;
using Dalamud.Game.Command;
using Dalamud.Game.Internal;
using Dalamud.Interface;
+using Dalamud.Plugin.Sanitizer;
namespace Dalamud.Plugin
{
@@ -48,6 +49,7 @@ namespace Dalamud.Plugin
this.pluginName = pluginName;
this.configs = configs;
+ this.Sanitizer = new Sanitizer.Sanitizer(this.Data.Language);
this.UiLanguage = this.dalamud.Configuration.LanguageOverride;
dalamud.LocalizationManager.OnLocalizationChanged += this.OnLocalizationChanged;
}
@@ -132,6 +134,11 @@ namespace Dalamud.Plugin
///
public string UiLanguage { get; private set; }
+ ///
+ /// Gets serializer class with functions to remove special characters from strings.
+ ///
+ public ISanitizer Sanitizer { get; }
+
///
/// Gets the action that should be executed when any plugin sends a message.
///
diff --git a/Dalamud/Plugin/Sanitizer/ISanitizer.cs b/Dalamud/Plugin/Sanitizer/ISanitizer.cs
new file mode 100644
index 000000000..21fe8ab77
--- /dev/null
+++ b/Dalamud/Plugin/Sanitizer/ISanitizer.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+
+namespace Dalamud.Plugin.Sanitizer
+{
+ ///
+ /// Sanitize strings to remove soft hyphens and other special characters.
+ ///
+ public interface ISanitizer
+ {
+ ///
+ /// Creates a sanitized string using current clientLanguage.
+ ///
+ /// An unsanitized string to sanitize.
+ /// A sanitized string.
+ string Sanitize(string unsanitizedString);
+
+ ///
+ /// Creates a sanitized string using request clientLanguage.
+ ///
+ /// An unsanitized string to sanitize.
+ /// Target language for sanitized strings.
+ /// A sanitized string.
+ string Sanitize(string unsanitizedString, ClientLanguage clientLanguage);
+
+ ///
+ /// Creates a list of sanitized strings using current clientLanguage.
+ ///
+ /// List of unsanitized string to sanitize.
+ /// A list of sanitized strings.
+ IEnumerable Sanitize(IEnumerable unsanitizedStrings);
+
+ ///
+ /// Creates a list of sanitized strings using requested clientLanguage.
+ ///
+ /// List of unsanitized string to sanitize.
+ /// Target language for sanitized strings.
+ /// A list of sanitized strings.
+ IEnumerable Sanitize(IEnumerable unsanitizedStrings, ClientLanguage clientLanguage);
+ }
+}
diff --git a/Dalamud/Plugin/Sanitizer/Sanitizer.cs b/Dalamud/Plugin/Sanitizer/Sanitizer.cs
new file mode 100644
index 000000000..66b2c413f
--- /dev/null
+++ b/Dalamud/Plugin/Sanitizer/Sanitizer.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Dalamud.Plugin.Sanitizer
+{
+ ///
+ /// Sanitize strings to remove soft hyphens and other special characters.
+ ///
+ public class Sanitizer : ISanitizer
+ {
+ private readonly KeyValuePair softHyphen = new KeyValuePair("\u0002\u0016\u0001\u0003", string.Empty);
+ private readonly KeyValuePair emphasisOpen = new KeyValuePair("\u0002\u001A\u0003", string.Empty);
+ private readonly KeyValuePair emphasisClose = new KeyValuePair("\u0002\u001A\u0001\u0003", string.Empty);
+ private readonly KeyValuePair indent = new KeyValuePair("\u0002\u001D\u0001\u0003", string.Empty);
+ private readonly KeyValuePair dagger = new KeyValuePair("\u0020\u2020", string.Empty);
+ private readonly KeyValuePair ligatureOE = new KeyValuePair("\u0153", "\u006F\u0065");
+ private readonly List> defaultSanitizationList;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Default clientLanguage for sanitizing strings.
+ public Sanitizer(ClientLanguage clientLanguage)
+ {
+ this.defaultSanitizationList = this.BuildSanitizationList(clientLanguage);
+ }
+
+ ///
+ /// Creates a sanitized string using current clientLanguage.
+ ///
+ /// An unsanitized string to sanitize.
+ /// A sanitized string.
+ public string Sanitize(string unsanitizedString)
+ {
+ return this.defaultSanitizationList == null ? unsanitizedString : ApplySanitizationList(unsanitizedString, this.defaultSanitizationList);
+ }
+
+ ///
+ /// Creates a sanitized string using request clientLanguage.
+ ///
+ /// An unsanitized string to sanitize.
+ /// Target language for sanitized strings.
+ /// A sanitized string.
+ public string Sanitize(string unsanitizedString, ClientLanguage clientLanguage)
+ {
+ var newSanitizationList = this.BuildSanitizationList(clientLanguage);
+ return newSanitizationList == null ? unsanitizedString : ApplySanitizationList(unsanitizedString, newSanitizationList);
+ }
+
+ ///
+ /// Creates a list of sanitized strings using current clientLanguage.
+ ///
+ /// List of unsanitized string to sanitize.
+ /// A list of sanitized strings.
+ public IEnumerable Sanitize(IEnumerable unsanitizedStrings)
+ {
+ return this.defaultSanitizationList == null ? unsanitizedStrings.Select(unsanitizedString => unsanitizedString) :
+ unsanitizedStrings.Select(unsanitizedString => ApplySanitizationList(unsanitizedString, this.defaultSanitizationList));
+ }
+
+ ///
+ /// Creates a list of sanitized strings using requested clientLanguage.
+ ///
+ /// List of unsanitized string to sanitize.
+ /// Target language for sanitized strings.
+ /// A list of sanitized strings.
+ public IEnumerable Sanitize(IEnumerable unsanitizedStrings, ClientLanguage clientLanguage)
+ {
+ var newSanitizationList = this.BuildSanitizationList(clientLanguage);
+ return newSanitizationList == null ? unsanitizedStrings.Select(unsanitizedString => unsanitizedString) :
+ unsanitizedStrings.Select(unsanitizedString => ApplySanitizationList(unsanitizedString, newSanitizationList));
+ }
+
+ private static string ApplySanitizationList(string unsanitizedString, IEnumerable> sanitizationList)
+ {
+ var sanitizedValue = new StringBuilder(unsanitizedString);
+ foreach (var item in sanitizationList) sanitizedValue.Replace(item.Key, item.Value);
+ return sanitizedValue.ToString();
+ }
+
+ private List> BuildSanitizationList(ClientLanguage clientLanguage)
+ {
+ switch (clientLanguage)
+ {
+ case ClientLanguage.Japanese:
+ break;
+ case ClientLanguage.English:
+ break;
+ case ClientLanguage.German:
+ return new List>
+ {
+ this.softHyphen,
+ this.emphasisOpen,
+ this.emphasisClose,
+ this.dagger,
+ };
+ case ClientLanguage.French:
+ return new List>
+ {
+ this.softHyphen,
+ this.indent,
+ this.ligatureOE,
+ };
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return null;
+ }
+ }
+}