Add language-aware article sorting

This commit is contained in:
ajp_anton
2026-07-19 01:35:02 +00:00
parent 6c33dc1d69
commit 6a81debc8c
8 changed files with 237 additions and 37 deletions
@@ -29,6 +29,9 @@ public static class ItemsProxySorting
}
public static void Apply(JsonNode root, ItemsProxyControls controls, CultureInfo culture, string[] titleArticles)
=> Apply(root, controls, culture, _ => titleArticles);
public static void Apply(JsonNode root, ItemsProxyControls controls, CultureInfo culture, Func<JsonObject, string[]> titleArticlesForItem)
{
if (!TryGetItemsArray(root, out var itemsArray))
return;
@@ -36,9 +39,9 @@ public static class ItemsProxySorting
var items = itemsArray.OfType<JsonObject>().ToList();
var specs = BuildSortSpecs(controls.SortBy);
if (specs.Length > 0 && items.Count > 1)
items = Sort(items, specs, controls.SortOrder, culture, titleArticles);
items = Sort(items, specs, controls.SortOrder, culture, titleArticlesForItem);
items = FilterByNameStartsWith(items, controls.NameStartsWith, culture, titleArticles);
items = FilterByNameStartsWith(items, controls.NameStartsWith, culture, titleArticlesForItem);
var filteredCount = items.Count;
items = Slice(items, controls.StartIndex, controls.Limit);
@@ -77,24 +80,45 @@ public static class ItemsProxySorting
}
}
public static string[] GetSortArticles(SortArticleCatalog articleCatalog, IEnumerable<SortArticleEntry> configuredArticles, string? sortLocale)
public static string[] GetSortArticles(
SortArticleCatalog articleCatalog,
IEnumerable<SortArticleEntry> configuredArticles,
string? titleLanguage,
bool ignoreArticles = true)
{
var locale = (sortLocale ?? string.Empty).Trim();
if (locale.Length == 0)
locale = "en";
if (!ignoreArticles)
return [];
var configured = configuredArticles.ToArray();
var custom = configured.FirstOrDefault(e => e.Language.Equals(locale, StringComparison.OrdinalIgnoreCase))
?? configured.FirstOrDefault(e => locale.StartsWith(e.Language + "-", StringComparison.OrdinalIgnoreCase));
var always = configured
.Where(e => e.AlwaysApply == true)
.SelectMany(e => SplitArticles(e.Articles));
return always
.Concat(GetLanguageArticles(articleCatalog, configured, titleLanguage))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
}
private static string[] GetLanguageArticles(
SortArticleCatalog articleCatalog,
SortArticleEntry[] configured,
string? titleLanguage)
{
var language = (titleLanguage ?? string.Empty).Trim();
if (language.Length == 0)
return [];
var custom = configured.FirstOrDefault(e => e.Language.Equals(language, StringComparison.OrdinalIgnoreCase))
?? configured.FirstOrDefault(e => language.StartsWith(e.Language + "-", StringComparison.OrdinalIgnoreCase));
if (custom is not null)
return SplitArticles(custom.Articles);
var builtIns = articleCatalog.GetBuiltIns();
if (builtIns.TryGetValue(locale, out var exact))
if (builtIns.TryGetValue(language, out var exact))
return exact;
var dash = locale.IndexOf('-', StringComparison.Ordinal);
return dash > 0 && builtIns.TryGetValue(locale[..dash], out var languageOnly)
var dash = language.IndexOf('-', StringComparison.Ordinal);
return dash > 0 && builtIns.TryGetValue(language[..dash], out var languageOnly)
? languageOnly
: [];
}
@@ -149,12 +173,12 @@ public static class ItemsProxySorting
_ => SortKind.Text
};
private static List<JsonObject> Sort(List<JsonObject> items, SortSpec[] specs, string sortOrder, CultureInfo culture, string[] titleArticles)
private static List<JsonObject> Sort(List<JsonObject> items, SortSpec[] specs, string sortOrder, CultureInfo culture, Func<JsonObject, string[]> titleArticlesForItem)
{
var descending = sortOrder.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.FirstOrDefault()?.Equals("Descending", StringComparison.OrdinalIgnoreCase) == true;
var comparer = StringComparer.Create(culture, true);
var values = items.Select(item => new { Item = item, Values = specs.Select(spec => ValueFor(item, spec, culture, titleArticles)).ToArray() }).ToList();
var values = items.Select(item => new { Item = item, Values = specs.Select(spec => ValueFor(item, spec, culture, titleArticlesForItem(item))).ToArray() }).ToList();
values.Sort((left, right) =>
{
@@ -197,14 +221,14 @@ public static class ItemsProxySorting
private readonly record struct SortValue(bool HasValue, string Text, double Number);
private static List<JsonObject> FilterByNameStartsWith(List<JsonObject> items, string prefix, CultureInfo culture, string[] titleArticles)
private static List<JsonObject> FilterByNameStartsWith(List<JsonObject> items, string prefix, CultureInfo culture, Func<JsonObject, string[]> titleArticlesForItem)
{
prefix = (prefix ?? string.Empty).Trim();
if (prefix.Length == 0)
return items;
return items
.Where(i => culture.CompareInfo.IsPrefix(StripArticle(GetString(i, "Name"), titleArticles), prefix, CompareOptions.IgnoreCase))
.Where(i => culture.CompareInfo.IsPrefix(StripArticle(GetString(i, "Name"), titleArticlesForItem(i)), prefix, CompareOptions.IgnoreCase))
.ToList();
}
@@ -227,8 +251,9 @@ public static class ItemsProxySorting
var word = article.Trim();
if (word.Length == 0 || text.Length <= word.Length)
continue;
if (text.StartsWith(word + " ", StringComparison.OrdinalIgnoreCase))
return text[(word.Length + 1)..].TrimStart();
var attachedArticle = word.EndsWith("'", StringComparison.Ordinal);
if (text.StartsWith(word + (attachedArticle ? string.Empty : " "), StringComparison.OrdinalIgnoreCase))
return text[word.Length..].TrimStart();
}
return text;