diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs
index da9873d8d..5101657ba 100644
--- a/Dalamud/Game/ClientState/ClientState.cs
+++ b/Dalamud/Game/ClientState/ClientState.cs
@@ -158,7 +158,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
ConditionFlag.NormalConditions,
ConditionFlag.Jumping,
ConditionFlag.Mounted,
- ConditionFlag.UsingParasol]);
+ ConditionFlag.UsingFashionAccessory]);
blockingFlag = blockingConditions.FirstOrDefault();
return blockingFlag == 0;
diff --git a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs
index 7003893ff..b9633d6e3 100644
--- a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs
+++ b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs
@@ -939,9 +939,7 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator
if (p.Type == ReadOnlySePayloadType.Text)
{
- context.Builder.Append(
- context.CultureInfo.TextInfo.ToTitleCase(Encoding.UTF8.GetString(p.Body.Span)));
-
+ context.Builder.Append(Encoding.UTF8.GetString(p.Body.Span).ToUpper(true, true, false, context.Language));
continue;
}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs
index bc0bd0ac9..3cb5d3242 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs
@@ -9,6 +9,7 @@ using Dalamud.Game.Text.Noun.Enums;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
+
using Lumina.Data;
using Lumina.Excel;
using Lumina.Excel.Sheets;
@@ -21,7 +22,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
internal class NounProcessorWidget : IDataWindowWidget
{
/// A list of German grammatical cases.
- internal static readonly string[] GermanCases = ["Nominative", "Genitive", "Dative", "Accusative"];
+ internal static readonly string[] GermanCases = [string.Empty, "Nominative", "Genitive", "Dative", "Accusative"];
private static readonly Type[] NounSheets = [
typeof(Aetheryte),
@@ -156,7 +157,7 @@ internal class NounProcessorWidget : IDataWindowWidget
GrammaticalCase = grammaticalCase,
};
var output = nounProcessor.ProcessNoun(nounParams).ExtractText().Replace("\"", "\\\"");
- var caseParam = language == ClientLanguage.German ? $"(int)GermanCases.{GermanCases[grammaticalCase]}" : "1";
+ var caseParam = language == ClientLanguage.German ? $"(int)GermanCases.{GermanCases[grammaticalCase + 1]}" : "1";
sb.AppendLine($"new(nameof(LSheets.{sheetType.Name}), {this.rowId}, ClientLanguage.{language}, {this.amount}, (int){articleTypeEnumType.Name}.{Enum.GetName(articleTypeEnumType, articleType)}, {caseParam}, \"{output}\"),");
}
}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs
index 92e57ddac..2175b8be1 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs
@@ -1014,7 +1014,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget
ImGui.TextUnformatted(Enum.GetName(articleTypeEnumType, u32));
}
- if (macroCode is MacroCode.DeNoun && exprIdx == 4 && u32 is >= 0 and <= 3)
+ if (macroCode is MacroCode.DeNoun && exprIdx == 4 && u32 is >= 0 and <= 4)
{
ImGui.SameLine();
ImGui.TextUnformatted(NounProcessorWidget.GermanCases[u32]);
diff --git a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs
index ce135b947..d40184a76 100644
--- a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs
+++ b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs
@@ -501,7 +501,7 @@ internal class AutoUpdateManager : IServiceType
condition.OnlyAny(ConditionFlag.NormalConditions,
ConditionFlag.Jumping,
ConditionFlag.Mounted,
- ConditionFlag.UsingParasol);
+ ConditionFlag.UsingFashionAccessory);
}
private bool IsPluginManagerReady()
diff --git a/Dalamud/Utility/StringExtensions.cs b/Dalamud/Utility/StringExtensions.cs
index 50973e338..c28aebab2 100644
--- a/Dalamud/Utility/StringExtensions.cs
+++ b/Dalamud/Utility/StringExtensions.cs
@@ -1,5 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
+using System.Text;
+
+using Dalamud.Game;
using FFXIVClientStructs.FFXIV.Client.UI;
@@ -10,6 +13,9 @@ namespace Dalamud.Utility;
///
public static class StringExtensions
{
+ private static readonly string[] CommonExcludedWords = ["sas", "zos", "van", "nan", "tol", "deus", "mal", "de", "rem", "out", "yae", "bas", "cen", "quo", "viator", "la"];
+ private static readonly string[] EnglishExcludedWords = ["of", "the", "to", "and", "a", "an", "or", "at", "by", "for", "in", "on", "with", "from", .. CommonExcludedWords];
+
///
/// An extension method to chain usage of string.Format.
///
@@ -77,7 +83,7 @@ public static class StringExtensions
public static string StripSoftHyphen(this string input) => input.Replace("\u00AD", string.Empty);
///
- /// Truncates the given string to the specified maximum number of characters,
+ /// Truncates the given string to the specified maximum number of characters,
/// appending an ellipsis if truncation occurs.
///
/// The string to truncate.
@@ -88,4 +94,128 @@ public static class StringExtensions
{
return string.IsNullOrEmpty(input) || input.Length <= maxChars ? input : input[..maxChars] + ellipses;
}
+
+ ///
+ /// Converts the input string to uppercase based on specified options like capitalizing the first character,
+ /// normalizing vowels, and excluding certain words based on the selected language.
+ ///
+ /// The input string to be converted to uppercase.
+ /// Whether to capitalize only the first character of the string.
+ /// Whether to capitalize the first letter of each word.
+ /// Whether to normalize vowels to uppercase if they appear at the beginning of a word.
+ /// The language context used to determine which words to exclude from capitalization.
+ /// A new string with the appropriate characters converted to uppercase.
+ /// This is a C# implementation of Client::System::String::Utf8String.ToUpper with word exclusion lists as used by the HeadAll macro.
+ public static string ToUpper(this string input, bool firstCharOnly, bool everyWord, bool normalizeVowels, ClientLanguage language)
+ {
+ return ToUpper(input, firstCharOnly, everyWord, normalizeVowels, language switch
+ {
+ ClientLanguage.Japanese => [],
+ ClientLanguage.English => EnglishExcludedWords,
+ ClientLanguage.German => CommonExcludedWords,
+ ClientLanguage.French => CommonExcludedWords,
+ _ => [],
+ });
+ }
+
+ ///
+ /// Converts the input string to uppercase based on specified options like capitalizing the first character,
+ /// normalizing vowels, and excluding certain words based on the selected language.
+ ///
+ /// The input string to be converted to uppercase.
+ /// Whether to capitalize only the first character of the string.
+ /// Whether to capitalize the first letter of each word.
+ /// Whether to normalize vowels to uppercase if they appear at the beginning of a word.
+ /// A list of words to exclude from being capitalized. Words in this list will remain lowercase.
+ /// A new string with the appropriate characters converted to uppercase.
+ /// This is a C# implementation of Client::System::String::Utf8String.ToUpper.
+ public static string ToUpper(this string input, bool firstCharOnly, bool everyWord, bool normalizeVowels, ReadOnlySpan excludedWords)
+ {
+ if (string.IsNullOrEmpty(input))
+ return input;
+
+ var builder = new StringBuilder(input);
+ var isWordBeginning = true;
+ var length = firstCharOnly && !everyWord ? 1 : builder.Length;
+
+ for (var i = 0; i < length; i++)
+ {
+ var ch = builder[i];
+
+ if (ch == ' ')
+ {
+ isWordBeginning = true;
+ continue;
+ }
+
+ if (firstCharOnly && !isWordBeginning)
+ continue;
+
+ // Basic ASCII a-z
+ if (ch >= 'a' && ch <= 'z')
+ {
+ var substr = builder.ToString(i, builder.Length - i);
+ var isExcluded = false;
+
+ // Do not exclude words at the beginning
+ if (i > 0)
+ {
+ foreach (var excludedWord in excludedWords)
+ {
+ if (substr.StartsWith(excludedWord + " ", StringComparison.OrdinalIgnoreCase))
+ {
+ isExcluded = true;
+ break;
+ }
+ }
+ }
+
+ if (!isExcluded)
+ {
+ builder[i] = char.ToUpperInvariant(ch);
+ }
+ }
+
+ // Special œ → Œ
+ else if (ch == 'œ')
+ {
+ builder[i] = 'Œ';
+ }
+
+ // Characters with accents
+ else if (ch >= 'à' && ch <= 'ý' && ch != '÷')
+ {
+ builder[i] = char.ToUpperInvariant(ch);
+ }
+
+ // Normalize vowels with accents
+ else if (normalizeVowels && isWordBeginning)
+ {
+ if ("àáâãäå".Contains(ch))
+ {
+ builder[i] = 'A';
+ }
+ else if ("èéêë".Contains(ch))
+ {
+ builder[i] = 'E';
+ }
+ else if ("ìíîï".Contains(ch))
+ {
+ builder[i] = 'I';
+ }
+ else if ("òóôõö".Contains(ch))
+ {
+ builder[i] = 'O';
+ }
+ else if ("ùúûü".Contains(ch))
+ {
+ builder[i] = 'U';
+ }
+ }
+
+ isWordBeginning = false;
+ }
+
+ return builder.ToString();
+ }
}