120 lines
4.8 KiB
C#
120 lines
4.8 KiB
C#
namespace PlotLine.Services;
|
|
|
|
public static class CharacterSexHintService
|
|
{
|
|
public const string Unknown = "Unknown";
|
|
public const string Female = "Female";
|
|
public const string Male = "Male";
|
|
public const string NonBinaryOrOther = "NonBinaryOrOther";
|
|
|
|
private static readonly HashSet<string> FemaleNames = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"abby", "abigail", "ada", "adele", "alice", "alison", "allison", "amanda", "amelia", "amy",
|
|
"angela", "anna", "anne", "annie", "barbara", "becky", "belinda", "beth", "bethany", "brenda",
|
|
"carol", "caroline", "catherine", "charlotte", "chloe", "chrissy", "christine", "cindy", "claire", "clara",
|
|
"daisy", "debbie", "debby", "deborah", "diana", "diane", "donna", "dorothy", "elaine", "elizabeth",
|
|
"ella", "ellie", "emily", "emma", "eva", "eve", "fiona", "gemma", "georgina", "grace", "hannah",
|
|
"harriet", "heather", "helen", "isabel", "isabella", "jacqueline", "jane", "janet", "janice",
|
|
"jean", "jennifer", "jenny", "jessica", "joanne", "judy", "julia", "julie", "karen", "kate", "katie",
|
|
"katherine", "kathryn", "kelly", "laura", "lesley", "linda", "lisa", "lily", "louise", "lucy",
|
|
"maggie", "margaret", "maria", "marie", "marion", "mary", "michelle", "molly", "nicola", "olivia",
|
|
"pamela", "patricia", "paula", "rachel", "rebecca", "rose", "sally", "samantha", "sandra", "sarah",
|
|
"shirley", "sophie", "stephanie", "susan", "suzanne", "tracey", "tracy", "valerie", "victoria",
|
|
"wendy", "zoe"
|
|
};
|
|
|
|
private static readonly HashSet<string> MaleNames = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"adam", "alan", "albert", "alexander", "andrew", "anthony", "arthur", "barry", "ben", "benjamin",
|
|
"bernard", "brian", "bruce", "charles", "chris", "christopher", "colin", "craig", "daniel", "darren",
|
|
"david", "dennis", "douglas", "edward", "eric", "frank", "gary", "geoff", "geoffrey", "george",
|
|
"graham", "grant", "greg", "harry", "henry", "howard", "ian", "jack", "james", "john", "jonathan", "joseph",
|
|
"keith", "kenneth", "kevin", "liam", "martin", "mark", "matthew", "michael", "neil", "nick", "nicolas",
|
|
"nicholas", "nigel", "oliver", "paul", "peter", "phil", "philip", "raymond", "richard", "robert", "roger",
|
|
"ronald", "samuel", "simon", "stephen", "steven", "stuart", "terence", "thomas", "tim", "timothy",
|
|
"tom", "tony", "william"
|
|
};
|
|
|
|
private static readonly HashSet<string> AmbiguousNames = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"alex", "andy", "ashley", "billie", "blair", "bobbie", "casey", "charlie", "drew", "frankie",
|
|
"jamie", "jessie", "jo", "jordan", "lee", "leslie", "morgan", "pat", "riley", "robyn", "robin",
|
|
"sam", "sandy", "shannon", "taylor", "terry"
|
|
};
|
|
|
|
private static readonly Dictionary<string, string> TitleHints = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
["mr"] = Male,
|
|
["mister"] = Male,
|
|
["sir"] = Male,
|
|
["mrs"] = Female,
|
|
["miss"] = Female,
|
|
["ms"] = Female,
|
|
["madam"] = Female,
|
|
["lady"] = Female
|
|
};
|
|
|
|
public static string Suggest(string? name)
|
|
{
|
|
var token = FirstNameToken(name, out var titleHint);
|
|
if (!string.IsNullOrWhiteSpace(titleHint))
|
|
{
|
|
return titleHint;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(token) || AmbiguousNames.Contains(token))
|
|
{
|
|
return Unknown;
|
|
}
|
|
|
|
if (FemaleNames.Contains(token))
|
|
{
|
|
return Female;
|
|
}
|
|
|
|
return MaleNames.Contains(token) ? Male : Unknown;
|
|
}
|
|
|
|
public static string Normalise(string? value, string? fallbackName = null)
|
|
=> value?.Trim() switch
|
|
{
|
|
Female => Female,
|
|
Male => Male,
|
|
NonBinaryOrOther => NonBinaryOrOther,
|
|
Unknown => Unknown,
|
|
_ => Suggest(fallbackName)
|
|
};
|
|
|
|
private static string FirstNameToken(string? name, out string? titleHint)
|
|
{
|
|
titleHint = null;
|
|
var clean = name?.Trim();
|
|
if (string.IsNullOrWhiteSpace(clean))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
var tokens = clean
|
|
.Split([' ', '-', '_'], StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(CleanToken)
|
|
.Where(token => !string.IsNullOrWhiteSpace(token))
|
|
.ToList();
|
|
if (tokens.Count == 0)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
if (TitleHints.TryGetValue(tokens[0], out titleHint))
|
|
{
|
|
return tokens.Skip(1).FirstOrDefault() ?? string.Empty;
|
|
}
|
|
|
|
return tokens[0];
|
|
}
|
|
|
|
private static string CleanToken(string token)
|
|
{
|
|
return token.Trim('.', ',', ';', ':', '\'', '"', '(', ')', '[', ']').ToLowerInvariant();
|
|
}
|
|
}
|