Skip to content

Commit 2eeb641

Browse files
committed
Fix format string index issue
1 parent f8b14b4 commit 2eeb641

File tree

1 file changed

+155
-54
lines changed

1 file changed

+155
-54
lines changed

Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs

Lines changed: 155 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -153,22 +153,146 @@ private static ImmutableArray<LocalizableString> ParseXamlFile(AdditionalText fi
153153

154154
if (key != null)
155155
{
156-
localizableStrings.Add(ParseLocalizableString(key, value, comment));
156+
var formatParams = GetParameters(value);
157+
var (summary, updatedFormatParams) = ParseCommentAndUpdateParameters(comment, formatParams);
158+
localizableStrings.Add(new LocalizableString(key, value, summary, updatedFormatParams));
157159
}
158160
}
159161

160162
return localizableStrings.ToImmutableArray();
161163
}
162164

163-
private static LocalizableString ParseLocalizableString(string key, string value, XComment comment)
165+
/// <summary>
166+
/// Analyzes the format string and returns a list of its parameters.
167+
/// </summary>
168+
/// <param name="format"></param>
169+
/// <returns></returns>
170+
private static List<LocalizableStringParam> GetParameters(string format)
164171
{
165-
var (summary, parameters) = ParseComment(comment);
166-
return new LocalizableString(key, value, summary, parameters);
172+
var parameters = new Dictionary<int, string>();
173+
int maxIndex = -1;
174+
int i = 0;
175+
int len = format.Length;
176+
177+
while (i < len)
178+
{
179+
if (format[i] == '{')
180+
{
181+
if (i + 1 < len && format[i + 1] == '{')
182+
{
183+
// Escaped '{', skip both
184+
i += 2;
185+
continue;
186+
}
187+
else
188+
{
189+
// Start of a format item, parse index and format
190+
i++; // Move past '{'
191+
int index = 0;
192+
bool hasIndex = false;
193+
// Parse index
194+
while (i < len && char.IsDigit(format[i]))
195+
{
196+
hasIndex = true;
197+
index = index * 10 + (format[i] - '0');
198+
i++;
199+
}
200+
if (!hasIndex)
201+
{
202+
// Skip invalid format item
203+
while (i < len && format[i] != '}')
204+
{
205+
i++;
206+
}
207+
if (i < len)
208+
{
209+
i++; // Move past '}'
210+
}
211+
continue;
212+
}
213+
// Check for colon to parse format
214+
string formatPart = null;
215+
if (i < len && format[i] == ':')
216+
{
217+
i++; // Move past ':'
218+
int start = i;
219+
while (i < len && format[i] != '}')
220+
{
221+
i++;
222+
}
223+
formatPart = i < len ? format.Substring(start, i - start) : format.Substring(start);
224+
if (i < len)
225+
{
226+
i++; // Move past '}'
227+
}
228+
}
229+
else if (i < len && format[i] == '}')
230+
{
231+
// No format part
232+
i++; // Move past '}'
233+
}
234+
else
235+
{
236+
// Invalid characters after index, skip to '}'
237+
while (i < len && format[i] != '}')
238+
{
239+
i++;
240+
}
241+
if (i < len)
242+
{
243+
i++; // Move past '}'
244+
}
245+
}
246+
parameters[index] = formatPart;
247+
if (index > maxIndex)
248+
{
249+
maxIndex = index;
250+
}
251+
}
252+
}
253+
else if (format[i] == '}')
254+
{
255+
// Handle possible escaped '}}'
256+
if (i + 1 < len && format[i + 1] == '}')
257+
{
258+
i += 2;
259+
}
260+
else
261+
{
262+
i++;
263+
}
264+
}
265+
else
266+
{
267+
i++;
268+
}
269+
}
270+
271+
// Generate the result list from 0 to maxIndex
272+
var result = new List<LocalizableStringParam>();
273+
if (maxIndex == -1)
274+
{
275+
return result;
276+
}
277+
278+
for (int idx = 0; idx <= maxIndex; idx++)
279+
{
280+
var formatValue = parameters.TryGetValue(idx, out var value) ? value : null;
281+
result.Add(new LocalizableStringParam { Index = idx, Format = formatValue, Name = $"arg{idx}", Type = "object?" });
282+
}
283+
284+
return result;
167285
}
168286

169-
private static (string Summary, ImmutableArray<LocalizableStringParam> Parameters) ParseComment(XComment comment)
287+
/// <summary>
288+
/// Parses the comment and updates the format parameter names and types.
289+
/// </summary>
290+
/// <param name="comment"></param>
291+
/// <param name="parameters"></param>
292+
/// <returns></returns>
293+
private static (string Summary, ImmutableArray<LocalizableStringParam> Parameters) ParseCommentAndUpdateParameters(XComment comment, List<LocalizableStringParam> parameters)
170294
{
171-
if (comment == null || comment.Value == null)
295+
if (comment == null || comment.Value == null || parameters.Count == 0)
172296
{
173297
return (null, ImmutableArray<LocalizableStringParam>.Empty);
174298
}
@@ -177,13 +301,26 @@ private static (string Summary, ImmutableArray<LocalizableStringParam> Parameter
177301
{
178302
var doc = XDocument.Parse($"<root>{comment.Value}</root>");
179303
var summary = doc.Descendants("summary").FirstOrDefault()?.Value.Trim();
180-
var parameters = doc.Descendants("param")
181-
.Select(p => new LocalizableStringParam(
182-
int.Parse(p.Attribute("index").Value),
183-
p.Attribute("name").Value,
184-
p.Attribute("type").Value))
185-
.ToImmutableArray();
186-
return (summary, parameters);
304+
305+
// Update parameter names and types of the format string
306+
foreach (var p in doc.Descendants("param"))
307+
{
308+
var index = int.TryParse(p.Attribute("index").Value, out var intValue) ? intValue : -1;
309+
var name = p.Attribute("name").Value;
310+
var type = p.Attribute("type").Value;
311+
if (index >= 0 && index < parameters.Count)
312+
{
313+
if (!string.IsNullOrEmpty(name))
314+
{
315+
parameters[index].Name = name;
316+
}
317+
if (!string.IsNullOrEmpty(type))
318+
{
319+
parameters[index].Type = type;
320+
}
321+
}
322+
}
323+
return (summary, parameters.ToImmutableArray());
187324
}
188325
catch
189326
{
@@ -472,7 +609,7 @@ private static void GenerateLocalizationMethod(
472609
string tabString)
473610
{
474611
sb.Append($"{tabString}public static string {ls.Key}(");
475-
var parameters = BuildParameters(ls);
612+
var parameters = ls.Params.ToList();
476613
sb.Append(string.Join(", ", parameters.Select(p => $"{p.Type} {p.Name}")));
477614
sb.Append(") => ");
478615

@@ -500,24 +637,6 @@ private static void GenerateLocalizationMethod(
500637
sb.AppendLine();
501638
}
502639

503-
private static List<MethodParameter> BuildParameters(LocalizableString ls)
504-
{
505-
var parameters = new List<MethodParameter>();
506-
for (var i = 0; i < 10; i++)
507-
{
508-
if (!ls.Value.Contains($"{{{i}}}"))
509-
{
510-
continue;
511-
}
512-
513-
var param = ls.Params.FirstOrDefault(p => p.Index == i);
514-
parameters.Add(param is null
515-
? new MethodParameter($"arg{i}", "object?")
516-
: new MethodParameter(param.Name, param.Type));
517-
}
518-
return parameters;
519-
}
520-
521640
private static string Spacing(int n)
522641
{
523642
Span<char> spaces = stackalloc char[n * 4];
@@ -536,30 +655,12 @@ private static string Spacing(int n)
536655

537656
#region Classes
538657

539-
public class MethodParameter
540-
{
541-
public string Name { get; }
542-
public string Type { get; }
543-
544-
public MethodParameter(string name, string type)
545-
{
546-
Name = name;
547-
Type = type;
548-
}
549-
}
550-
551658
public class LocalizableStringParam
552659
{
553-
public int Index { get; }
554-
public string Name { get; }
555-
public string Type { get; }
556-
557-
public LocalizableStringParam(int index, string name, string type)
558-
{
559-
Index = index;
560-
Name = name;
561-
Type = type;
562-
}
660+
public int Index { get; set; }
661+
public string Format { get; set; }
662+
public string Name { get; set; }
663+
public string Type { get; set; }
563664
}
564665

565666
public class LocalizableString

0 commit comments

Comments
 (0)