Skip to content

Commit

Permalink
Merge branch 'main' into internals-enhancement
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonCropp authored May 30, 2024
2 parents 6600e82 + ed39e05 commit 6774cc5
Show file tree
Hide file tree
Showing 22 changed files with 159 additions and 111 deletions.
34 changes: 32 additions & 2 deletions NuSpecs/Humanizer.Core.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,45 @@
<language>en</language>
<repository type="$RepositoryType$" url="$RepositoryUrl$" commit="$RepositoryCommit$" />
<dependencies>
<group targetFramework="netstandard2.0" />
<group targetFramework="net6.0" />
<group targetFramework=".NETFramework4.8">
<dependency id="System.Collections.Immutable" version="8.0.0" exclude="Build,Analyzers" />
<dependency id="System.ComponentModel.Annotations" version="5.0.0" exclude="Build,Analyzers" />
<dependency id="System.Memory" version="4.5.5" exclude="Build,Analyzers" />
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
</group>
<group targetFramework="net6.0">
<dependency id="System.Collections.Immutable" version="8.0.0" exclude="Build,Analyzers" />
<dependency id="System.Memory" version="4.5.5" exclude="Build,Analyzers" />
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
</group>
<group targetFramework="net7.0">
<dependency id="System.Collections.Immutable" version="8.0.0" exclude="Build,Analyzers" />
<dependency id="System.Memory" version="4.5.5" exclude="Build,Analyzers" />
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
</group>
<group targetFramework="net8.0">
<dependency id="System.Memory" version="4.5.5" exclude="Build,Analyzers" />
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
</group>
<group targetFramework=".NETStandard2.0">
<dependency id="System.Collections.Immutable" version="8.0.0" exclude="Build,Analyzers" />
<dependency id="System.ComponentModel.Annotations" version="5.0.0" exclude="Build,Analyzers" />
<dependency id="System.Memory" version="4.5.5" exclude="Build,Analyzers" />
<dependency id="System.ValueTuple" version="4.5.0" exclude="Build,Analyzers" />
</group>
</dependencies>
</metadata>
<files>
<file src="Humanizer\bin\Release\netstandard2.0\*.dll" target="lib\netstandard2.0" />
<file src="Humanizer\bin\Release\netstandard2.0\*.xml" target="lib\netstandard2.0" />
<file src="Humanizer\bin\Release\net48\*.dll" target="lib\net48" />
<file src="Humanizer\bin\Release\net48\*.xml" target="lib\net48" />
<file src="Humanizer\bin\Release\net6.0\*.dll" target="lib\net6.0" />
<file src="Humanizer\bin\Release\net6.0\*.xml" target="lib\net6.0" />
<file src="Humanizer\bin\Release\net7.0\*.dll" target="lib\net7.0" />
<file src="Humanizer\bin\Release\net7.0\*.xml" target="lib\net7.0" />
<file src="Humanizer\bin\Release\net8.0\*.dll" target="lib\net8.0" />
<file src="Humanizer\bin\Release\net8.0\*.xml" target="lib\net8.0" />
<file src="..\logo.png" target="logo.png" />
</files>
</package>
18 changes: 8 additions & 10 deletions src/Humanizer/HeadingExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

/// <summary>
/// Style for the cardinal direction humanization
Expand All @@ -23,6 +23,7 @@ public enum HeadingStyle
public static class HeadingExtensions
{
internal static readonly string[] Headings = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"];
internal static readonly string[] HeadingsShort = ["N_Short", "NNE_Short", "NE_Short", "ENE_Short", "E_Short", "ESE_Short", "SE_Short", "SSE_Short", "S_Short", "SSW_Short", "SW_Short", "WSW_Short", "W_Short", "WNW_Short", "NW_Short", "NNW_Short"];
internal static readonly char[] HeadingArrows = ['↑', '↗', '→', '↘', '↓', '↙', '←', '↖'];

// https://stackoverflow.com/a/7490772/1720761
Expand All @@ -39,14 +40,11 @@ public static string ToHeading(this double heading, HeadingStyle style = Heading
{
var val = (int) (heading / 22.5 + .5);

var result = Headings[val % 16];
var headingsIndex = val % 16;

if (style == HeadingStyle.Abbreviated)
{
return Resources.GetResource($"{result}_Short", culture);
}

return Resources.GetResource(result, culture);
return Resources.GetResource(
style == HeadingStyle.Abbreviated ? HeadingsShort[headingsIndex] : Headings[headingsIndex],
culture);
}

/// <summary>
Expand Down Expand Up @@ -86,9 +84,9 @@ public static double FromAbbreviatedHeading(this string heading, CultureInfo? cu
culture ??= CultureInfo.CurrentCulture;

var upperCaseHeading = culture.TextInfo.ToUpper(heading);
for (var index = 0; index < Headings.Length; ++index)
for (var index = 0; index < HeadingsShort.Length; ++index)
{
var localizedShortHeading = Resources.GetResource($"{Headings[index]}_Short", culture);
var localizedShortHeading = Resources.GetResource(HeadingsShort[index], culture);
if (culture.CompareInfo.Compare(upperCaseHeading, localizedShortHeading) == 0)
{
return index * 22.5;
Expand Down
1 change: 1 addition & 0 deletions src/Humanizer/Humanizer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<NoWarn>CS1573;CS1591</NoWarn>
<IsTrimmable>true</IsTrimmable>
<PublicKey>0024000004800000940000000602000000240000525341310004000001000100F9104F5F9BDB168AE140366EB1CD84C469B020EA3423D5D29996D5214CE2BD9B7C0BA3EAD1CA545C4399668AB8911E61CC1CC83C7DF6D50FD6B781365B467E65173F40A11C957D27C5AA0CB0F8DA9C91C988203CC8AEF1468C74A472839D0FD870DA8D13A4DC6B3AAFDAF0384D8E18E393C613D88BF02A64467A119902204FCC</PublicKey>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Using Remove="System.Net.Http" />
Expand Down
4 changes: 2 additions & 2 deletions src/Humanizer/Inflections/Vocabulary.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

/// <summary>
/// A container for exceptions to simple pluralization/singularization rules.
Expand Down Expand Up @@ -185,7 +185,7 @@ bool IsUncountable(string word) =>

static string MatchUpperCase(string word, string replacement) =>
char.IsUpper(word[0]) &&
char.IsLower(replacement[0]) ? char.ToUpper(replacement[0]) + replacement.Substring(1) : replacement;
char.IsLower(replacement[0]) ? StringHumanizeExtensions.Concat(char.ToUpper(replacement[0]), replacement.AsSpan(1)) : replacement;

/// <summary>
/// If the word is the letter s, singular or plural, return the letter s singular
Expand Down
8 changes: 4 additions & 4 deletions src/Humanizer/InflectorExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//The Inflector class was cloned from Inflector (https://github.com/srkirkland/Inflector)
//The Inflector class was cloned from Inflector (https://github.com/srkirkland/Inflector)

//The MIT License (MIT)

Expand Down Expand Up @@ -65,9 +65,9 @@ public static string Camelize(this string input)
{
var word = input.Pascalize();
return word.Length > 0
? word
.Substring(0, 1)
.ToLower() + word.Substring(1)
? StringHumanizeExtensions.Concat(
char.ToLower(word[0]),
word.AsSpan(1))
: word;
}

Expand Down
24 changes: 21 additions & 3 deletions src/Humanizer/Localisation/Formatters/DefaultFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

/// <summary>
/// Default implementation of IFormatter interface.
Expand Down Expand Up @@ -50,11 +50,29 @@ public virtual string TimeSpanHumanize_Age()
/// <inheritdoc cref="IFormatter.DataUnitHumanize(DataUnit, double, bool)"/>
public virtual string DataUnitHumanize(DataUnit dataUnit, double count, bool toSymbol = true)
{
var resourceKey = toSymbol ? $"DataUnit_{dataUnit}Symbol" : $"DataUnit_{dataUnit}";
var resourceKey = (toSymbol, dataUnit) switch
{
(true, DataUnit.Bit) => "DataUnit_BitSymbol",
(true, DataUnit.Byte) => "DataUnit_ByteSymbol",
(true, DataUnit.Kilobyte) => "DataUnit_KilobyteSymbol",
(true, DataUnit.Megabyte) => "DataUnit_MegabyteSymbol",
(true, DataUnit.Gigabyte) => "DataUnit_GigabyteSymbol",
(true, DataUnit.Terabyte) => "DataUnit_TerabyteSymbol",
(true, _) => $"DataUnit_{dataUnit}Symbol",

(false, DataUnit.Bit) => "DataUnit_Bit",
(false, DataUnit.Byte) => "DataUnit_Byte",
(false, DataUnit.Kilobyte) => "DataUnit_Kilobyte",
(false, DataUnit.Megabyte) => "DataUnit_Megabyte",
(false, DataUnit.Gigabyte) => "DataUnit_Gigabyte",
(false, DataUnit.Terabyte) => "DataUnit_Terabyte",
(false, _) => $"DataUnit_{dataUnit}",
};

var resourceValue = Format(resourceKey);

if (!toSymbol && count > 1)
resourceValue += 's';
resourceValue += "s";

return resourceValue;
}
Expand Down
10 changes: 5 additions & 5 deletions src/Humanizer/Localisation/Formatters/LuxembourgishFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class LuxembourgishFormatter(CultureInfo culture) :
DefaultFormatter(culture)
Expand All @@ -18,12 +18,12 @@ public static string ApplyEifelerRule(string word)
=> word.TrimEnd(EifelerRuleSuffix);

public static string CheckForAndApplyEifelerRule(string word, string nextWord)
=> DoesEifelerRuleApply(nextWord)
=> DoesEifelerRuleApply(nextWord.AsSpan())
? word.TrimEnd(EifelerRuleSuffix)
: word;

public static bool DoesEifelerRuleApply(string nextWord)
=> !string.IsNullOrWhiteSpace(nextWord)
public static bool DoesEifelerRuleApply(CharSpan nextWord)
=> !nextWord.IsWhiteSpace()
&& !EifelerRuleCharacters.Contains(nextWord[0]);

protected override string Format(TimeUnit unit, string resourceKey, int number, bool toWords = false)
Expand All @@ -33,7 +33,7 @@ protected override string Format(TimeUnit unit, string resourceKey, int number,

return string.Format(resourceString,
toWords ? numberAsWord : number,
DoesEifelerRuleApply(numberAsWord) ? "" : EifelerRuleSuffix);
DoesEifelerRuleApply(numberAsWord.AsSpan()) ? "" : EifelerRuleSuffix);
}

protected override string GetResourceKey(string resourceKey, int number)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class ArabicNumberToWordsConverter :
GenderedNumberToWordsConverter
Expand Down Expand Up @@ -268,7 +268,9 @@ static string ParseNumber(string word, int number, GrammaticalGender gender)
foreach (var kv in ordinals.Where(kv => word.EndsWith(kv.Key)))
{
// replace word with exception
return word.Substring(0, word.Length - kv.Key.Length) + kv.Value;
return StringHumanizeExtensions.Concat(
word.AsSpan(0, word.Length - kv.Key.Length),
kv.Value.AsSpan());
}
}
else if (number is > 10 and < 100)
Expand All @@ -286,7 +288,9 @@ static string ParseNumber(string word, int number, GrammaticalGender gender)
foreach (var kv in ordinals.Where(kv => oldPart.EndsWith(kv.Key)))
{
// replace word with exception
newPart = oldPart.Substring(0, oldPart.Length - kv.Key.Length) + kv.Value;
newPart = StringHumanizeExtensions.Concat(
oldPart.AsSpan(0, oldPart.Length - kv.Key.Length),
kv.Value.AsSpan());
}

if (number > 19 && newPart == oldPart && oldPart.Length > 1)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class AzerbaijaniNumberToWordsConverter :
GenderlessNumberToWordsConverter
Expand Down Expand Up @@ -116,7 +116,7 @@ public override string ConvertToOrdinal(int number)

if (word[^1] == 't')
{
word = word.Substring(0, word.Length - 1) + 'd';
word = StringHumanizeExtensions.Concat(word.AsSpan(0, word.Length - 1), 'd');
}

if (suffixFoundOnLastVowel)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class BrazilianPortugueseNumberToWordsConverter :
GenderedNumberToWordsConverter
Expand Down Expand Up @@ -166,17 +166,17 @@ static string ApplyGender(string toWords, GrammaticalGender gender)

if (toWords.EndsWith("os"))
{
return toWords.Substring(0, toWords.Length - 2) + "as";
return StringHumanizeExtensions.Concat(toWords.AsSpan(0, toWords.Length - 2), "as".AsSpan());
}

if (toWords.EndsWith("um"))
{
return toWords.Substring(0, toWords.Length - 2) + "uma";
return StringHumanizeExtensions.Concat(toWords.AsSpan(0, toWords.Length - 2), "uma".AsSpan());
}

if (toWords.EndsWith("dois"))
{
return toWords.Substring(0, toWords.Length - 4) + "duas";
return StringHumanizeExtensions.Concat(toWords.AsSpan(0, toWords.Length - 4), "duas".AsSpan());
}

return toWords;
Expand All @@ -186,7 +186,9 @@ static string ApplyOrdinalGender(string toWords, GrammaticalGender gender)
{
if (gender == GrammaticalGender.Feminine)
{
return toWords.TrimEnd('o') + 'a';
return StringHumanizeExtensions.Concat(
toWords.AsSpan().TrimEnd('o'),
'a');
}

return toWords;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

/// <summary>
/// Dutch spelling of numbers is not really officially regulated.
Expand Down Expand Up @@ -170,7 +170,7 @@ public override string ConvertToOrdinal(int number)
foreach (var kv in OrdinalExceptions.Where(kv => word.EndsWith(kv.Key)))
{
// replace word with exception
return word.Substring(0, word.Length - kv.Key.Length) + kv.Value;
return StringHumanizeExtensions.Concat(word.AsSpan(0, word.Length - kv.Key.Length), kv.Value.AsSpan());
}

// achtste
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,11 @@
namespace Humanizer;
namespace Humanizer;

class EnglishNumberToWordsConverter : GenderlessNumberToWordsConverter
{
static readonly string[] UnitsMap = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"];
static readonly string[] UnitsMapTh = ["zeroth", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth"];
static readonly string[] TensMap = ["zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];

static readonly Dictionary<long, string> OrdinalExceptions = new()
{
{
1, "first"
},
{
2, "second"
},
{
3, "third"
},
{
4, "fourth"
},
{
5, "fifth"
},
{
8, "eighth"
},
{
9, "ninth"
},
{
12, "twelfth"
},
};

public override string Convert(long number) =>
Convert(number, false);

Expand Down Expand Up @@ -126,23 +99,8 @@ static void CollectPartsUnderAThousand(List<string> parts, long number, bool isO
}
}

static string GetUnitValue(long number, bool isOrdinal)
{
if (isOrdinal)
{
if (ExceptionNumbersToWords(number, out var exceptionString))
{
return exceptionString;
}

return $"{UnitsMap[number]}th";
}

return UnitsMap[number];
}

static bool ExceptionNumbersToWords(long number, [NotNullWhen(true)] out string? words) =>
OrdinalExceptions.TryGetValue(number, out words);
static string GetUnitValue(long number, bool isOrdinal) =>
isOrdinal ? UnitsMapTh[number] : UnitsMap[number];

public override string ConvertToTuple(int number) =>
number switch
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class LuxembourgishNumberToWordsConverter : GenderedNumberToWordsConverter
{
Expand Down Expand Up @@ -166,7 +166,7 @@ private static string GetEndingForGender(GrammaticalGender gender) =>
private string GetPartWithEifelerRule(string pluralFormat, long number, GrammaticalGender gender)
{
var nextWord = pluralFormat
.Substring(3, pluralFormat.Length - 3)
.AsSpan(3, pluralFormat.Length - 3)
.TrimStart();
var wordForm = LuxembourgishFormatter.DoesEifelerRuleApply(nextWord)
? WordForm.Eifeler
Expand Down
Loading

0 comments on commit 6774cc5

Please sign in to comment.