1+ using System . Collections . Generic ;
12using System . Text ;
23using System . Text . RegularExpressions ;
34
@@ -6,9 +7,14 @@ namespace MelonAccessibilityLib
67 /// <summary>
78 /// Utility class to clean text for screen reader output.
89 /// Removes Unity rich text tags, normalizes whitespace, and handles escape sequences.
10+ /// Consumers can extend cleaning behavior via <see cref="AddReplacement"/> and <see cref="AddRegexReplacement(string, string, RegexOptions)"/>.
911 /// </summary>
1012 public static class TextCleaner
1113 {
14+ private static readonly object _lock = new object ( ) ;
15+ private static readonly List < KeyValuePair < string , string > > _customReplacements = new List < KeyValuePair < string , string > > ( ) ;
16+ private static readonly List < KeyValuePair < Regex , string > > _customRegexReplacements = new List < KeyValuePair < Regex , string > > ( ) ;
17+
1218 // Unity rich text tags
1319 private static readonly Regex ColorTagRegex = new Regex (
1420 @"<color[^>]*>|</color>" ,
@@ -47,8 +53,99 @@ public static class TextCleaner
4753 RegexOptions . Compiled
4854 ) ;
4955
56+ /// <summary>
57+ /// Adds a custom string replacement that will be applied during text cleaning.
58+ /// Replacements are applied in the order they are added, after Unity rich text tags are removed.
59+ /// </summary>
60+ /// <param name="find">The string to find.</param>
61+ /// <param name="replace">The string to replace it with.</param>
62+ public static void AddReplacement ( string find , string replace )
63+ {
64+ if ( string . IsNullOrEmpty ( find ) )
65+ return ;
66+
67+ lock ( _lock )
68+ {
69+ _customReplacements . Add ( new KeyValuePair < string , string > ( find , replace ?? string . Empty ) ) ;
70+ }
71+ }
72+
73+ /// <summary>
74+ /// Adds a custom regex replacement that will be applied during text cleaning.
75+ /// Regex replacements are applied in the order they are added, after string replacements.
76+ /// </summary>
77+ /// <param name="pattern">The regex pattern to match.</param>
78+ /// <param name="replace">The replacement string (supports regex substitutions like $1).</param>
79+ /// <param name="options">Optional regex options. Defaults to <see cref="RegexOptions.None"/>.</param>
80+ public static void AddRegexReplacement ( string pattern , string replace , RegexOptions options = RegexOptions . None )
81+ {
82+ if ( string . IsNullOrEmpty ( pattern ) )
83+ return ;
84+
85+ lock ( _lock )
86+ {
87+ _customRegexReplacements . Add ( new KeyValuePair < Regex , string > (
88+ new Regex ( pattern , options ) ,
89+ replace ?? string . Empty
90+ ) ) ;
91+ }
92+ }
93+
94+ /// <summary>
95+ /// Adds a custom regex replacement that will be applied during text cleaning.
96+ /// Regex replacements are applied in the order they are added, after string replacements.
97+ /// </summary>
98+ /// <param name="regex">The compiled regex to use for matching.</param>
99+ /// <param name="replace">The replacement string (supports regex substitutions like $1).</param>
100+ public static void AddRegexReplacement ( Regex regex , string replace )
101+ {
102+ if ( regex == null )
103+ return ;
104+
105+ lock ( _lock )
106+ {
107+ _customRegexReplacements . Add ( new KeyValuePair < Regex , string > ( regex , replace ?? string . Empty ) ) ;
108+ }
109+ }
110+
111+ /// <summary>
112+ /// Removes all custom string replacements (not regex replacements).
113+ /// </summary>
114+ public static void ClearReplacements ( )
115+ {
116+ lock ( _lock )
117+ {
118+ _customReplacements . Clear ( ) ;
119+ }
120+ }
121+
122+ /// <summary>
123+ /// Removes all custom regex replacements (not string replacements).
124+ /// </summary>
125+ public static void ClearRegexReplacements ( )
126+ {
127+ lock ( _lock )
128+ {
129+ _customRegexReplacements . Clear ( ) ;
130+ }
131+ }
132+
133+ /// <summary>
134+ /// Removes all custom replacements (both string and regex).
135+ /// </summary>
136+ public static void ClearAllCustomReplacements ( )
137+ {
138+ lock ( _lock )
139+ {
140+ _customReplacements . Clear ( ) ;
141+ _customRegexReplacements . Clear ( ) ;
142+ }
143+ }
144+
50145 /// <summary>
51146 /// Clean text by removing Unity rich text tags and normalizing whitespace.
147+ /// Custom replacements registered via <see cref="AddReplacement"/> and <see cref="AddRegexReplacement(string, string, RegexOptions)"/>
148+ /// are applied after tag removal and before whitespace normalization.
52149 /// </summary>
53150 public static string Clean ( string input )
54151 {
@@ -71,6 +168,9 @@ public static string Clean(string input)
71168 // Handle escape sequences
72169 cleaned = UnescapeText ( cleaned ) ;
73170
171+ // Apply custom replacements
172+ cleaned = ApplyCustomReplacements ( cleaned ) ;
173+
74174 // Normalize whitespace
75175 cleaned = MultipleSpacesRegex . Replace ( cleaned , " " ) ;
76176
@@ -94,6 +194,31 @@ private static string UnescapeText(string input)
94194 return sb . ToString ( ) ;
95195 }
96196
197+ private static string ApplyCustomReplacements ( string input )
198+ {
199+ if ( string . IsNullOrEmpty ( input ) )
200+ return input ;
201+
202+ string result = input ;
203+
204+ lock ( _lock )
205+ {
206+ // Apply string replacements first
207+ foreach ( var replacement in _customReplacements )
208+ {
209+ result = result . Replace ( replacement . Key , replacement . Value ) ;
210+ }
211+
212+ // Apply regex replacements
213+ foreach ( var replacement in _customRegexReplacements )
214+ {
215+ result = replacement . Key . Replace ( result , replacement . Value ) ;
216+ }
217+ }
218+
219+ return result ;
220+ }
221+
97222 /// <summary>
98223 /// Combine multiple lines into a single string, cleaning each line.
99224 /// </summary>
0 commit comments