Scott Hanselman

Fun with Noun Pluralization libraries and the .NET Framework

February 2, '11 Comments [15] Posted in BCL
Sponsored By

Add Entity Pluralization 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. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Wednesday, February 02, 2011 10:38:02 PM UTC
This is a very good improvment from .net framework 3 to net framework 4. Most nice think is that I do not need to change the words manually beacuse it is nice to know If we are gonna get multiple or single. E.g.

This is better
Add(Reservation reservation);
than
Add(Reservations reservation);
Wednesday, February 02, 2011 10:40:07 PM UTC
I can see how that would be useful! They should have that in the core framework.
Wednesday, February 02, 2011 11:00:15 PM UTC
Note that if you were going to use something like this to produce localized messages (using a language-specific pluralizer), other languages can have very different rules about when to use plural vs. singular and even how many plural forms there are.

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.
Thursday, February 03, 2011 7:49:56 AM UTC
Personally I think having libraries in .NET for language related functionality like this is way overdue. Maybe this can be a start of something good?
As always, great work there, Scott.
Thursday, February 03, 2011 1:02:19 PM UTC
I definitely think this functionality should be embraced by the namespace gods and put somewhere that's a little more obvious. It's funny, after working on a Rails project just the other day and using the built-in pluralize function quite a bit I was thinking to myself, "Why doesn't the .NET Framework have this?" Seeing more and more similarities between Ruby and .NET (which is a great thing!)
Thursday, February 03, 2011 2:32:08 PM UTC
I find the Porter Stemmer more useful in my work. Does the .Net framework have this native? The library can be found at http://tartarus.org/~martin/PorterStemmer/ this takes a word and gives you the root word. Combine this with a thesaurus and spell checker and you have a nice tool as a expansion pass for a natural language parser or context definer. I hadn't seen the pluralize function, I'll have to add that :-) Thanks.
Thursday, February 03, 2011 5:22:13 PM UTC
I remember a couple of months ago, I was doing a project using Entity Framework. There was this table called Movies when you create an entity for Movies, the EF would name it as Movy. I used to rename it, but everytime I update my EF, it would name it back to Movy.
sandeep
Thursday, February 03, 2011 11:18:48 PM UTC
Have you tried "octopus"? Did it return "octopuses", "octopi" or "octopodes"?

See www.youtube.com/watch?v=wFyY2mK8pxk for one of the best English lessons ever.



Friday, February 04, 2011 5:53:55 PM UTC
It does have a factory method, but it only supports English: PluralizationService.CreateService(cultureInfo). Calling it with another culture will throw a NotImplementedException with the info "We don't support locales other than english yet." Pretty lame.

I walked through creating a simple FrenchPluralizationService for when it does become available.
Friday, February 04, 2011 7:12:01 PM UTC
@Chris Eargle - even within the english world is it really a good idea. i don't think so.

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
DaveWill
Friday, February 04, 2011 8:02:44 PM UTC
Yep, because I didn't know about this little hidden feature, I'd rejected a late change request to singularise some content for page titles on a project :(

Could probably have said yes for English for if I'd known.
Monday, February 07, 2011 9:39:08 AM UTC
I write a veterinary app -- "Specimen" singularizes to "Speciman". I've left that stet in the source code because I find it kind of funny.

@Serendata, Octopus -> Octopuses, according to System.Data.Entity.Design. I hope I don't ever need an Octopus table, though.

g.
Friday, February 18, 2011 6:32:09 PM UTC
Nice! I've just plonked this in a static class, and written a method and an extension method for int to make it a little more ruby on rails like. This first determines whether it should be a plural or not, and then calls the right method.


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")
Friday, March 04, 2011 1:48:47 PM UTC
I've created a small library call Pluralizer that wraps the PluralizationService (but also adds more) and allows you to use it to write full sentences, much like you would with String.Format.

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
Tuesday, September 06, 2011 11:59:39 PM UTC
Hi hanselman,
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.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.