From 2eeb641a18df2fe29f679093990f6a3a71300f03 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 8 Mar 2025 17:54:07 +0800 Subject: [PATCH 1/4] Fix format string index issue --- .../Localize/LocalizeSourceGenerator.cs | 209 +++++++++++++----- 1 file changed, 155 insertions(+), 54 deletions(-) diff --git a/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs b/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs index 1d48f6e..123dcb0 100644 --- a/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs +++ b/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs @@ -153,22 +153,146 @@ private static ImmutableArray ParseXamlFile(AdditionalText fi if (key != null) { - localizableStrings.Add(ParseLocalizableString(key, value, comment)); + var formatParams = GetParameters(value); + var (summary, updatedFormatParams) = ParseCommentAndUpdateParameters(comment, formatParams); + localizableStrings.Add(new LocalizableString(key, value, summary, updatedFormatParams)); } } return localizableStrings.ToImmutableArray(); } - private static LocalizableString ParseLocalizableString(string key, string value, XComment comment) + /// + /// Analyzes the format string and returns a list of its parameters. + /// + /// + /// + private static List GetParameters(string format) { - var (summary, parameters) = ParseComment(comment); - return new LocalizableString(key, value, summary, parameters); + var parameters = new Dictionary(); + int maxIndex = -1; + int i = 0; + int len = format.Length; + + while (i < len) + { + if (format[i] == '{') + { + if (i + 1 < len && format[i + 1] == '{') + { + // Escaped '{', skip both + i += 2; + continue; + } + else + { + // Start of a format item, parse index and format + i++; // Move past '{' + int index = 0; + bool hasIndex = false; + // Parse index + while (i < len && char.IsDigit(format[i])) + { + hasIndex = true; + index = index * 10 + (format[i] - '0'); + i++; + } + if (!hasIndex) + { + // Skip invalid format item + while (i < len && format[i] != '}') + { + i++; + } + if (i < len) + { + i++; // Move past '}' + } + continue; + } + // Check for colon to parse format + string formatPart = null; + if (i < len && format[i] == ':') + { + i++; // Move past ':' + int start = i; + while (i < len && format[i] != '}') + { + i++; + } + formatPart = i < len ? format.Substring(start, i - start) : format.Substring(start); + if (i < len) + { + i++; // Move past '}' + } + } + else if (i < len && format[i] == '}') + { + // No format part + i++; // Move past '}' + } + else + { + // Invalid characters after index, skip to '}' + while (i < len && format[i] != '}') + { + i++; + } + if (i < len) + { + i++; // Move past '}' + } + } + parameters[index] = formatPart; + if (index > maxIndex) + { + maxIndex = index; + } + } + } + else if (format[i] == '}') + { + // Handle possible escaped '}}' + if (i + 1 < len && format[i + 1] == '}') + { + i += 2; + } + else + { + i++; + } + } + else + { + i++; + } + } + + // Generate the result list from 0 to maxIndex + var result = new List(); + if (maxIndex == -1) + { + return result; + } + + for (int idx = 0; idx <= maxIndex; idx++) + { + var formatValue = parameters.TryGetValue(idx, out var value) ? value : null; + result.Add(new LocalizableStringParam { Index = idx, Format = formatValue, Name = $"arg{idx}", Type = "object?" }); + } + + return result; } - private static (string Summary, ImmutableArray Parameters) ParseComment(XComment comment) + /// + /// Parses the comment and updates the format parameter names and types. + /// + /// + /// + /// + private static (string Summary, ImmutableArray Parameters) ParseCommentAndUpdateParameters(XComment comment, List parameters) { - if (comment == null || comment.Value == null) + if (comment == null || comment.Value == null || parameters.Count == 0) { return (null, ImmutableArray.Empty); } @@ -177,13 +301,26 @@ private static (string Summary, ImmutableArray Parameter { var doc = XDocument.Parse($"{comment.Value}"); var summary = doc.Descendants("summary").FirstOrDefault()?.Value.Trim(); - var parameters = doc.Descendants("param") - .Select(p => new LocalizableStringParam( - int.Parse(p.Attribute("index").Value), - p.Attribute("name").Value, - p.Attribute("type").Value)) - .ToImmutableArray(); - return (summary, parameters); + + // Update parameter names and types of the format string + foreach (var p in doc.Descendants("param")) + { + var index = int.TryParse(p.Attribute("index").Value, out var intValue) ? intValue : -1; + var name = p.Attribute("name").Value; + var type = p.Attribute("type").Value; + if (index >= 0 && index < parameters.Count) + { + if (!string.IsNullOrEmpty(name)) + { + parameters[index].Name = name; + } + if (!string.IsNullOrEmpty(type)) + { + parameters[index].Type = type; + } + } + } + return (summary, parameters.ToImmutableArray()); } catch { @@ -472,7 +609,7 @@ private static void GenerateLocalizationMethod( string tabString) { sb.Append($"{tabString}public static string {ls.Key}("); - var parameters = BuildParameters(ls); + var parameters = ls.Params.ToList(); sb.Append(string.Join(", ", parameters.Select(p => $"{p.Type} {p.Name}"))); sb.Append(") => "); @@ -500,24 +637,6 @@ private static void GenerateLocalizationMethod( sb.AppendLine(); } - private static List BuildParameters(LocalizableString ls) - { - var parameters = new List(); - for (var i = 0; i < 10; i++) - { - if (!ls.Value.Contains($"{{{i}}}")) - { - continue; - } - - var param = ls.Params.FirstOrDefault(p => p.Index == i); - parameters.Add(param is null - ? new MethodParameter($"arg{i}", "object?") - : new MethodParameter(param.Name, param.Type)); - } - return parameters; - } - private static string Spacing(int n) { Span spaces = stackalloc char[n * 4]; @@ -536,30 +655,12 @@ private static string Spacing(int n) #region Classes - public class MethodParameter - { - public string Name { get; } - public string Type { get; } - - public MethodParameter(string name, string type) - { - Name = name; - Type = type; - } - } - public class LocalizableStringParam { - public int Index { get; } - public string Name { get; } - public string Type { get; } - - public LocalizableStringParam(int index, string name, string type) - { - Index = index; - Name = name; - Type = type; - } + public int Index { get; set; } + public string Format { get; set; } + public string Name { get; set; } + public string Type { get; set; } } public class LocalizableString From e1cfe811898de7365f115cad81a5dfca3ab6907e Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 8 Mar 2025 18:23:10 +0800 Subject: [PATCH 2/4] Add culture info when need to add format --- .../Localize/LocalizeSourceGenerator.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs b/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs index 123dcb0..65e30c0 100644 --- a/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs +++ b/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs @@ -609,10 +609,11 @@ private static void GenerateLocalizationMethod( string tabString) { sb.Append($"{tabString}public static string {ls.Key}("); + + // Get parameter string var parameters = ls.Params.ToList(); sb.Append(string.Join(", ", parameters.Select(p => $"{p.Type} {p.Name}"))); sb.Append(") => "); - var formatArgs = parameters.Count > 0 ? $", {string.Join(", ", parameters.Select(p => p.Name))}" : string.Empty; @@ -620,13 +621,17 @@ private static void GenerateLocalizationMethod( if (isCoreAssembly) { sb.AppendLine(parameters.Count > 0 - ? $"string.Format(InternationalizationManager.Instance.GetTranslation(\"{ls.Key}\"){formatArgs});" + ? !ls.Format ? + $"string.Format(InternationalizationManager.Instance.GetTranslation(\"{ls.Key}\"){formatArgs});" + : $"string.Format(System.Globalization.CultureInfo.CurrentCulture, InternationalizationManager.Instance.GetTranslation(\"{ls.Key}\"){formatArgs});" : $"InternationalizationManager.Instance.GetTranslation(\"{ls.Key}\");"); } else if (pluginInfo?.IsValid == true) { sb.AppendLine(parameters.Count > 0 - ? $"string.Format({pluginInfo.ContextAccessor}.API.GetTranslation(\"{ls.Key}\"){formatArgs});" + ? !ls.Format ? + $"string.Format({pluginInfo.ContextAccessor}.API.GetTranslation(\"{ls.Key}\"){formatArgs});" + : $"string.Format(System.Globalization.CultureInfo.CurrentCulture, {pluginInfo.ContextAccessor}.API.GetTranslation(\"{ls.Key}\"){formatArgs});" : $"{pluginInfo.ContextAccessor}.API.GetTranslation(\"{ls.Key}\");"); } else @@ -669,6 +674,8 @@ public class LocalizableString public string Value { get; } public string Summary { get; } public IEnumerable Params { get; } + + public bool Format => Params.Any(p => !string.IsNullOrEmpty(p.Format)); public LocalizableString(string key, string value, string summary, IEnumerable @params) { From ce8ff621fd5bde9866944ad51f3c4f69663707e6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 8 Mar 2025 18:35:53 +0800 Subject: [PATCH 3/4] Improve code quality --- .../Localize/LocalizeSourceGenerator.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs b/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs index 65e30c0..291172c 100644 --- a/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs +++ b/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs @@ -620,19 +620,21 @@ private static void GenerateLocalizationMethod( if (isCoreAssembly) { + var getTranslation = "InternationalizationManager.Instance.GetTranslation"; sb.AppendLine(parameters.Count > 0 ? !ls.Format ? - $"string.Format(InternationalizationManager.Instance.GetTranslation(\"{ls.Key}\"){formatArgs});" - : $"string.Format(System.Globalization.CultureInfo.CurrentCulture, InternationalizationManager.Instance.GetTranslation(\"{ls.Key}\"){formatArgs});" - : $"InternationalizationManager.Instance.GetTranslation(\"{ls.Key}\");"); + $"string.Format({getTranslation}(\"{ls.Key}\"){formatArgs});" + : $"string.Format(System.Globalization.CultureInfo.CurrentCulture, {getTranslation}(\"{ls.Key}\"){formatArgs});" + : $"{getTranslation}(\"{ls.Key}\");"); } else if (pluginInfo?.IsValid == true) { + var getTranslation = $"{pluginInfo.ContextAccessor}.API.GetTranslation"; sb.AppendLine(parameters.Count > 0 ? !ls.Format ? - $"string.Format({pluginInfo.ContextAccessor}.API.GetTranslation(\"{ls.Key}\"){formatArgs});" - : $"string.Format(System.Globalization.CultureInfo.CurrentCulture, {pluginInfo.ContextAccessor}.API.GetTranslation(\"{ls.Key}\"){formatArgs});" - : $"{pluginInfo.ContextAccessor}.API.GetTranslation(\"{ls.Key}\");"); + $"string.Format({getTranslation}(\"{ls.Key}\"){formatArgs});" + : $"string.Format(System.Globalization.CultureInfo.CurrentCulture, {getTranslation}(\"{ls.Key}\"){formatArgs});" + : $"{getTranslation}(\"{ls.Key}\");"); } else { From 671fc758c17844f6334a57c6269a945edd6011ac Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 9 Mar 2025 11:59:16 +0800 Subject: [PATCH 4/4] Support alignment format string & Improve documents --- .../Localize/LocalizeSourceGenerator.cs | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs b/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs index 291172c..0c46972 100644 --- a/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs +++ b/Flow.Launcher.Localization.SourceGenerators/Localize/LocalizeSourceGenerator.cs @@ -190,6 +190,7 @@ private static List GetParameters(string format) i++; // Move past '{' int index = 0; bool hasIndex = false; + // Parse index while (i < len && char.IsDigit(format[i])) { @@ -197,6 +198,7 @@ private static List GetParameters(string format) index = index * 10 + (format[i] - '0'); i++; } + if (!hasIndex) { // Skip invalid format item @@ -210,8 +212,25 @@ private static List GetParameters(string format) } continue; } - // Check for colon to parse format + + // Check for alignment (comma followed by optional sign and digits) + if (i < len && format[i] == ',') + { + i++; // Skip comma and optional sign + if (i < len && (format[i] == '+' || format[i] == '-')) + { + i++; + } + // Skip digits + while (i < len && char.IsDigit(format[i])) + { + i++; + } + } + string formatPart = null; + + // Check for format (after colon) if (i < len && format[i] == ':') { i++; // Move past ':' @@ -243,6 +262,7 @@ private static List GetParameters(string format) i++; // Move past '}' } } + parameters[index] = formatPart; if (index > maxIndex) { @@ -255,11 +275,11 @@ private static List GetParameters(string format) // Handle possible escaped '}}' if (i + 1 < len && format[i + 1] == '}') { - i += 2; + i += 2; // Skip escaped '}}' } else { - i++; + i++; // Move past '}' } } else @@ -274,7 +294,7 @@ private static List GetParameters(string format) { return result; } - + for (int idx = 0; idx <= maxIndex; idx++) { var formatValue = parameters.TryGetValue(idx, out var value) ? value : null;