From bd32454341b25e50eaf80fb972b461c44ae5b518 Mon Sep 17 00:00:00 2001
From: User <42337050+com-master@users.noreply.github.com>
Date: Mon, 15 Jun 2026 06:45:49 +0300
Subject: [PATCH] search char equivalences unicode
---
.../EntitySpawningUIController.cs | 6 +--
.../TileSpawningUIController.cs | 4 +-
Robust.Shared/Utility/SearchHelpers.cs | 41 +++++++++++++++++++
3 files changed, 46 insertions(+), 5 deletions(-)
create mode 100644 Robust.Shared/Utility/SearchHelpers.cs
diff --git a/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs b/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs
index dc596664d..ad03d3bdf 100644
--- a/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs
+++ b/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs
@@ -241,17 +241,17 @@ private static bool DoesEntityMatchSearch(EntityPrototype prototype, string sear
if (string.IsNullOrEmpty(searchStr))
return true;
- if (prototype.ID.Contains(searchStr, StringComparison.InvariantCultureIgnoreCase))
+ if (prototype.ID.ContainsSearch(searchStr))
return true;
if (prototype.EditorSuffix != null &&
- prototype.EditorSuffix.Contains(searchStr, StringComparison.InvariantCultureIgnoreCase))
+ prototype.EditorSuffix.ContainsSearch(searchStr))
return true;
if (string.IsNullOrEmpty(prototype.Name))
return false;
- if (prototype.Name.Contains(searchStr, StringComparison.InvariantCultureIgnoreCase))
+ if (prototype.Name.ContainsSearch(searchStr))
return true;
return false;
diff --git a/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs b/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs
index 3669b2491..ba49b1cff 100644
--- a/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs
+++ b/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs
@@ -221,8 +221,8 @@ private void BuildTileList(string? searchStr = null)
if (!string.IsNullOrEmpty(searchStr))
{
tileDefs = tileDefs.Where(s =>
- Loc.GetString(s.Name).Contains(searchStr, StringComparison.CurrentCultureIgnoreCase) ||
- s.ID.Contains(searchStr, StringComparison.OrdinalIgnoreCase));
+ Loc.GetString(s.Name).ContainsSearch(searchStr) ||
+ s.ID.ContainsSearch(searchStr));
}
tileDefs = tileDefs.OrderBy(d => Loc.GetString(d.Name));
diff --git a/Robust.Shared/Utility/SearchHelpers.cs b/Robust.Shared/Utility/SearchHelpers.cs
new file mode 100644
index 000000000..c636bfed9
--- /dev/null
+++ b/Robust.Shared/Utility/SearchHelpers.cs
@@ -0,0 +1,41 @@
+using System.Globalization;
+
+namespace Robust.Shared.Utility;
+
+///
+/// Helpers for user-facing text search that use Unicode-aware comparison, so that characters
+/// differing only by diacritical marks are treated as equivalent.
+///
+///
+/// Uses , ,
+/// and via . This means:
+///
+/// - Characters that differ only by diacritical marks are equivalent: Russian ё matches е,
+/// German ü matches u, French é/è/ê all match e, Spanish ñ matches n, etc.
+/// - Full-width and half-width variants of the same character are equivalent (relevant for
+/// Japanese and Chinese input).
+/// - Hiragana and Katakana representations of the same sound are equivalent.
+///
+/// Uses the current culture so that locale-specific comparison rules are respected.
+/// No configuration or setup is required; any UI code can call directly.
+///
+public static class SearchHelpers
+{
+ private const CompareOptions SearchOptions =
+ CompareOptions.IgnoreCase |
+ CompareOptions.IgnoreNonSpace |
+ CompareOptions.IgnoreWidth |
+ CompareOptions.IgnoreKanaType;
+
+ ///
+ /// Returns if contains
+ /// , ignoring case, diacritical marks, character width, and
+ /// kana type.
+ ///
+ public static bool ContainsSearch(this string source, string search)
+ {
+ if (string.IsNullOrEmpty(search)) return true;
+ if (string.IsNullOrEmpty(source)) return false;
+ return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, search, SearchOptions) >= 0;
+ }
+}