Skip to content

Commit c884371

Browse files
committed
Fix #744
Better support for escaping localized strings to prepare regexes
1 parent e55698c commit c884371

File tree

2 files changed

+164
-33
lines changed

2 files changed

+164
-33
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Microsoft.Build.Logging.StructuredLogger;
2+
using Xunit;
3+
4+
namespace StructuredLogger.Tests
5+
{
6+
public class ResourceStringTests
7+
{
8+
[Fact]
9+
public void TestInitialize()
10+
{
11+
var resources = StringsSet.ResourcesCollection;
12+
var cultures = resources.Keys;
13+
14+
foreach (var culture in cultures)
15+
{
16+
Strings.Initialize(culture);
17+
}
18+
}
19+
}
20+
}

src/StructuredLogger/Strings/Strings.cs

+144-33
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public static void Initialize(string culture = "en-US")
2323
}
2424
}
2525

26+
public static string Culture => ResourceSet?.Culture;
27+
2628
public static string GetString(string key)
2729
{
2830
return ResourceSet.GetString(key);
@@ -61,11 +63,7 @@ private static void InitializeRegex()
6163

6264
ProjectImported = GetString("ProjectImported");
6365

64-
string projectImported = "^" + ProjectImported
65-
.Replace(".", "\\.")
66-
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
67-
.Replace("{1}", @"(?<File>[^\""]+)")
68-
.Replace("({2},{3})", @"\((?<Line>\d+),(?<Column>\d+)\)") + "$";
66+
string projectImported = GetProjectImportedText();
6967
ProjectImportedRegex = new Regex(projectImported, RegexOptions.Compiled);
7068

7169
TargetSkippedFalseCondition = GetString("TargetSkippedFalseCondition");
@@ -95,7 +93,7 @@ private static void InitializeRegex()
9593

9694
RobocopyFileCopiedRegex = new Regex(Escape(RobocopyFileCopiedMessage)
9795
.Replace(@"\{0}", @"(?<From>[^\""]+)")
98-
.Replace(@"\{1}", @"(?<To>[^\""]+)"), RegexOptions.Compiled );
96+
.Replace(@"\{1}", @"(?<To>[^\""]+)"), RegexOptions.Compiled);
9997

10098
RobocopyFileSkippedRegex = new Regex(Escape(RobocopyFileSkippedMessage)
10199
.Replace(@"\{0}", @"(?<From>[^\""]+)")
@@ -124,29 +122,17 @@ private static void InitializeRegex()
124122
ProjectImportSkippedInvalidFileRegex = new Regex(skippedInvalidFile, RegexOptions.Compiled);
125123

126124
ProjectImportSkippedEmptyFile = GetString("ProjectImportSkippedEmptyFile");
127-
128-
string skippedEmptyFile = "^" + ProjectImportSkippedEmptyFile
129-
.Replace(".", "\\.")
130-
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
131-
.Replace("{1}", @"(?<File>[^\""]+)")
132-
.Replace("({2},{3})", @"\((?<Line>\d+),(?<Column>\d+)\)");
125+
var skippedEmptyFile = GetSkippedEmptyFileText();
133126
ProjectImportSkippedEmptyFileRegex = new Regex(skippedEmptyFile, RegexOptions.Compiled);
134127

135128
ProjectImportSkippedNoMatches = GetString("ProjectImportSkippedNoMatches");
136129

137-
string skippedNoMatches = "^" + ProjectImportSkippedNoMatches
138-
.Replace(".", "\\.")
139-
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
140-
.Replace("{1}", @"(?<File>.*)")
141-
.Replace("({2},{3})", @"\((?<Line>\d+),(?<Column>\d+)\)");
130+
string skippedNoMatches = GetSkippedNoMatchesText();
142131
ProjectImportSkippedNoMatchesRegex = new Regex(skippedNoMatches, RegexOptions.Compiled);
143132

144133
PropertyReassignment = GetString("PropertyReassignment");
145134

146-
string propertyReassignment = "^" + PropertyReassignment
147-
.Replace(@"$({0})=""{1}"" (", @"\$\((?<Name>\w+)\)="".*"" \(")
148-
.Replace(@"""{2}"")", @""".*""\)")
149-
.Replace("{3}", @"(?<File>.*) \((?<Line>\d+),(?<Column>\d+)\)$");
135+
string propertyReassignment = GetPropertyReassignmentText();
150136
PropertyReassignmentRegex = new Regex(propertyReassignment, RegexOptions.Compiled | RegexOptions.Singleline);
151137

152138
// MSBuild 17.6 shipped with this hardcoded to English (the first part of the regex), but it was switched to a different
@@ -174,13 +160,7 @@ private static void InitializeRegex()
174160

175161
ProjectImportSkippedFalseCondition = GetString("ProjectImportSkippedFalseCondition");
176162

177-
string skippedFalseCondition = "^" + ProjectImportSkippedFalseCondition
178-
.Replace(".", "\\.")
179-
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
180-
.Replace("{1}", @"(?<File>[^\""]+)")
181-
.Replace("({2},{3})", @"\((?<Line>\d+),(?<Column>\d+)\)")
182-
.Replace("{4}", "(?<Reason>.+)")
183-
.Replace("{5}", "(?<Evaluated>.+)");
163+
string skippedFalseCondition = GetSkippedFalseConditionText();
184164
ProjectImportSkippedFalseConditionRegex = new Regex(skippedFalseCondition, RegexOptions.Compiled);
185165

186166
CouldNotResolveSdk = GetString("CouldNotResolveSdk");
@@ -189,11 +169,7 @@ private static void InitializeRegex()
189169

190170
ProjectImportSkippedExpressionEvaluatedToEmpty = GetString("ProjectImportSkippedExpressionEvaluatedToEmpty");
191171

192-
string emptyCondition = "^" + ProjectImportSkippedExpressionEvaluatedToEmpty
193-
.Replace(".", "\\.")
194-
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
195-
.Replace("{1}", @"(?<File>[^\""]+)")
196-
.Replace("({2},{3})", @"\((?<Line>\d+),(?<Column>\d+)\)");
172+
string emptyCondition = GetEmptyConditionText();
197173
ProjectImportSkippedExpressionEvaluatedToEmptyRegex = new Regex(emptyCondition, RegexOptions.Compiled);
198174

199175
ConflictReferenceSameSDK = CreateRegex(GetString("GetSDKReferenceFiles.ConflictReferenceSameSDK"), 3);
@@ -229,6 +205,141 @@ private static void InitializeRegex()
229205
EvaluationFinished = GetString("EvaluationFinished");
230206
}
231207

208+
private static string GetEmptyConditionText()
209+
{
210+
if (Culture == "zh-Hans")
211+
{
212+
return "^" + ProjectImportSkippedExpressionEvaluatedToEmpty
213+
.Replace(".", "\\.")
214+
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
215+
.Replace("{1}", @"(?<File>[^\""]+)")
216+
.Replace("({2}、{3})", @"\((?<Line>\d+)、(?<Column>\d+)\)");
217+
}
218+
219+
return "^" + ProjectImportSkippedExpressionEvaluatedToEmpty
220+
.Replace(".", "\\.")
221+
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
222+
.Replace("{1}", @"(?<File>[^\""]+)")
223+
.Replace("({2},{3})", @"\((?<Line>\d+),(?<Column>\d+)\)");
224+
}
225+
226+
private static string GetSkippedFalseConditionText()
227+
{
228+
if (Culture == "zh-Hans")
229+
{
230+
return "^" + ProjectImportSkippedFalseCondition
231+
.Replace(".", "\\.")
232+
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
233+
.Replace("{1}", @"(?<File>[^\""]+)")
234+
.Replace("({2}、{3})", @"\((?<Line>\d+)、(?<Column>\d+)\)")
235+
.Replace("{4}", "(?<Reason>.+)")
236+
.Replace("{5}", "(?<Evaluated>.+)");
237+
}
238+
239+
return "^" + ProjectImportSkippedFalseCondition
240+
.Replace(".", "\\.")
241+
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
242+
.Replace("{1}", @"(?<File>[^\""]+)")
243+
.Replace("({2},{3})", @"\((?<Line>\d+),(?<Column>\d+)\)")
244+
.Replace("{4}", "(?<Reason>.+)")
245+
.Replace("{5}", "(?<Evaluated>.+)");
246+
}
247+
248+
private static string GetSkippedNoMatchesText()
249+
{
250+
if (Culture == "zh-Hans")
251+
{
252+
return "^" + ProjectImportSkippedNoMatches
253+
.Replace(".", "\\.")
254+
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
255+
.Replace("{1}", @"(?<File>.*)")
256+
.Replace("({2}、{3})", @"\((?<Line>\d+)、(?<Column>\d+)\)");
257+
}
258+
259+
return "^" + ProjectImportSkippedNoMatches
260+
.Replace(".", "\\.")
261+
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
262+
.Replace("{1}", @"(?<File>.*)")
263+
.Replace("({2},{3})", @"\((?<Line>\d+),(?<Column>\d+)\)");
264+
}
265+
266+
private static string GetSkippedEmptyFileText()
267+
{
268+
if (Culture == "zh-Hans")
269+
{
270+
return "^" + ProjectImportSkippedEmptyFile
271+
.Replace(".", "\\.")
272+
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
273+
.Replace("{1}", @"(?<File>[^\""]+)")
274+
.Replace("({2}、{3})", @"\((?<Line>\d+)、(?<Column>\d+)\)");
275+
}
276+
277+
return "^" + ProjectImportSkippedEmptyFile
278+
.Replace(".", "\\.")
279+
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
280+
.Replace("{1}", @"(?<File>[^\""]+)")
281+
.Replace("({2},{3})", @"\((?<Line>\d+),(?<Column>\d+)\)");
282+
}
283+
284+
private static string GetProjectImportedText()
285+
{
286+
if (Culture == "zh-Hans")
287+
{
288+
return "^" + ProjectImported
289+
.Replace(".", "\\.")
290+
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
291+
.Replace("{1}", @"(?<File>[^\""]+)")
292+
.Replace("({2}、{3})", @"\((?<Line>\d+)、(?<Column>\d+)\)") + "$";
293+
}
294+
295+
return "^" + ProjectImported
296+
.Replace(".", "\\.")
297+
.Replace("{0}", @"(?<ImportedProject>[^\""]+)")
298+
.Replace("{1}", @"(?<File>[^\""]+)")
299+
.Replace("({2},{3})", @"\((?<Line>\d+),(?<Column>\d+)\)") + "$";
300+
}
301+
302+
private static string GetPropertyReassignmentText()
303+
{
304+
string text = PropertyReassignment;
305+
306+
switch (Culture)
307+
{
308+
case "cs-CZ":
309+
text = text
310+
.Replace(@"$({0})={1} (", @"\$\((?<Name>\w+)\)="".*"" \(")
311+
.Replace(@"{2})", @".*\)")
312+
.Replace("{3}", @"(?<File>.*) \((?<Line>\d+),(?<Column>\d+)\)");
313+
break;
314+
case "ko-KR":
315+
text = text
316+
.Replace(@"$({0})={3}", @"\$\((?<Name>\w+)\)=(?<File>.*) \((?<Line>\d+),(?<Column>\d+)\)")
317+
.Replace("{1}", ".*")
318+
.Replace("{2}", ".*");
319+
break;
320+
case "pl-PL":
321+
text = text
322+
.Replace(@"$({0})=„{1}” (", @"\$\((?<Name>\w+)\)=„.*” \(")
323+
.Replace(@"„{2}”)", @"„.*”\)")
324+
.Replace("{3}", @"(?<File>.*) \((?<Line>\d+),(?<Column>\d+)\)");
325+
break;
326+
case "zh-Hans":
327+
text = text
328+
.Replace(@"$({0})=“{1}”(", @"\$\((?<Name>\w+)\)=“.*”\(")
329+
.Replace(@"{2}”)", @".*”\)")
330+
.Replace("{3}", @"(?<File>.*) \((?<Line>\d+),(?<Column>\d+)\)");
331+
break;
332+
default:
333+
text = text
334+
.Replace(@"$({0})=""{1}"" (", @"\$\((?<Name>\w+)\)="".*"" \(")
335+
.Replace(@"{2}"")", @".*""\)")
336+
.Replace("{3}", @"(?<File>.*) \((?<Line>\d+),(?<Column>\d+)\)");
337+
break;
338+
}
339+
340+
return "^" + text + "$";
341+
}
342+
232343
public static Regex CreateRegex(string text, int replacePlaceholders = 0, RegexOptions options = RegexOptions.Compiled)
233344
{
234345
text = Regex.Escape(text);

0 commit comments

Comments
 (0)