Fun with Noun Pluralization libraries and the .NET Framework
There was interesting discussion about noun pluralization on a mailing list today. One of the fun demos I throw into some talks is the automatic pluralization (taking something singular and making it plural) that's built into the Entity Framework design time tooling. For example, look at the screenshot to the right where a table of type "Goose" is called "Geese" as a set.
Dmitry released a nice "Simple English Noun Pluralizer" a few years back. His code is fun to read and clever for under 400 lines.
In addition to Dmitry's, in .NET 4 (Full Framework, not Client Profile) there's a little known PluralizationService inside of System.Data.Entity.Design that supports this user interface.
At this point there is only support for English and while the class is abstract, there's no proper provider model or factory. Also, personally I think service should be in the Base Class Library (BCL) and should been available as an extension method on string, and I've told them so. That said, it's still pretty cool and useful.
Create a new project in Visual Studio 2010. Right click on the project in the Solution Explorer, and from Properties select ".NET Framework 4" as the Target Framework. Otherwise you won't be able to see System.Data.Entity.Design in the list of .NET Assemblies from the Add Reference dialog. Add a reference, and go to town.
Here's some of the things I tried. I was impressed and confused when it changed Hanselman to Hanselmen. ;)
[Test]
public void AnimalsAreFunWhenPluralized()
{
var p = PluralizationService.CreateService(new CultureInfo("en-US"));
Assert.AreEqual("geese", p.Pluralize("goose"));
Assert.AreEqual("deer", p.Pluralize("deer"));
Assert.AreEqual("sheep", p.Pluralize("sheep"));
Assert.AreEqual("wolves", p.Pluralize("wolf"));
Assert.AreEqual("volcanoes", p.Pluralize("volcano"));
Assert.AreEqual("aircraft", p.Pluralize("aircraft"));
Assert.AreEqual("alumnae", p.Pluralize("alumna"));
Assert.AreEqual("alumni", p.Pluralize("alumnus"));
Assert.AreEqual("houses", p.Pluralize("house"));
Assert.AreEqual("fungi", p.Pluralize("fungus"));
Assert.AreEqual("Hanselmen", p.Pluralize("Hanselman"));
Assert.AreEqual("Hanselman", p.Singularize("Hanselmen"));
}
As I mentioned, If you wanted, you could derive from PluralizationService and create a Spanish or whatever-you-like service and plug it into the Entity Framework. Dmitry could even swap out the included service and broker calls to his own.
Note that this class was really just meant to be used from the EF Tooling, but I'd like to put gentle pressure on the Powers That Be (if this is useful) to put a little more work into it, make it a core thing, and make it work in more languages.
UPDATED: An interesting point from Jon Galloway. If you are using Entity Framework Code First, you can effectively disable the Pluralization Convention for Table Names by, ahem, removing the PluralizingTableNameConvention. Funny how that works, eh?
namespace MvcMusicStore.Models
{
public class MusicStoreEntities : DbContext
{
public DbSet<Album> Albums { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<Artist> Artists { get; set; }
public DbSet<Cart> Carts { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<OrderDetail> OrderDetails { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
Cool.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
For instance in some languages zero items is singular ('zero item'), and languages like Russian use relatively complex rules for determining plural form.
To support those scenarios, some localization frameworks like gettext take the number of items as a parameter and return a message in the proper plural form.
As always, great work there, Scott.
See www.youtube.com/watch?v=wFyY2mK8pxk for one of the best English lessons ever.
I walked through creating a simple FrenchPluralizationService for when it does become available.
Public Sub RedneckAnimalsAreFunWhenPluralized()
Dim p = PluralizationService.CreateService(New CultureInfo("en-US"))
Assert.AreEqual("skeeters", p.Pluralize("mosquito"))
Assert.AreEqual("coons", p.Pluralize("racoon"))
Assert.AreEqual("dawgs", p.Pluralize("dog"))
Assert.AreEqual("dawgs", p.Pluralize("blue tick hound"))
Assert.AreEqual("dawgs", p.Pluralize("golden retriever"))
Assert.AreEqual("lizards", p.Pluralize("iguana"))
Assert.AreEqual("lizards", p.Pluralize("skink"))
Assert.AreEqual("squirrels", p.Pluralize("sugar glider"))
Assert.AreEqual("cats", p.Pluralize("Exotic short hair Russian Blue cat"))
Assert.AreEqual("neighbor cook out night", p.Pluralize("possum"))
Assert.AreEqual("never again", p.Pluralize("crab"))
End Sub
Could probably have said yes for English for if I'd known.
@Serendata, Octopus -> Octopuses, according to System.Data.Entity.Design. I hope I don't ever need an Octopus table, though.
g.
public static class Grammar
{
private static readonly PluralizationService Pluralizer = PluralizationService.CreateService(new CultureInfo("en-US"));
public static string Pluralize(string word, int count)
{
return string.Format("{0} {1}", count, (count == 1 ? Pluralizer.Singularize(word) : Pluralizer.Pluralize(word)));
}
public static string Pluralize(this int count, string word)
{
return Pluralize(word, count);
}
}
Respectively, I can now call Grammar.Pluralize("goose", gooseCount) or 6.Pluralize("spider")
As a simplistic example:
var str = "There {is} {_} {person}.";
var single = Pluralizer.Instance.Pluralize(str, 1);
Assert.AreEqual("There is 1 person.", single);
var plural = Pluralizer.Instance.Pluralize(str, 67);
Assert.AreEqual("There are 67 people.", plural);
Have a look at my blog for a lot more info: Pluralizer
could you ask the ado.net team to provide a way to change Conventions and not just removing it on EF Code first?
And also a way to change the pluralization service to one other language? I have my on class based on PluralizationClass but after I remove the pluralization convention I cant add my own convention with my own pluralization class in Portuguese for example.
Thanks.
Comments are closed.
This is better
Add(Reservation reservation);
than
Add(Reservations reservation);