In my old Perl days I got a lot of great mileage out of the Lingua::EN::Inflect module, which helped write messages like so:
print "I saw $n ", PL("cat", $n), "\n";
and depending on the value of $n, that would generate "I saw 0 cats", "I saw 1 cat", "I saw 2 cats", etc.
Another precedent, but for R instead of Perl, is in the CLI library. It’s not quite as ambitious.
I’ve done a bunch of googling today to see whether Julia has a similar module or mechanism, but I’ve come up empty so far. I did find ResourceBundles and some gettext-based stuff, but can’t really see how to use those in this way.
If you don’t need very smart pluralization, it seems you could do this with a simple function and no need for a full package. For example:
julia> pluralize(n::Real, singular::AbstractString, plural::AbstractString=singular*"s") = n == 1 ? singular : plural
pluralize (generic function with 2 methods)
julia> n = 1; println("I saw $n $(pluralize(n, "cat"))!")
I saw 1 cat!
julia> n = 2; println("I saw $n $(pluralize(n, "cat"))!")
I saw 2 cats!
julia> n = 1; println("I saw $n $(pluralize(n, "doggy", "doggies"))!")
I saw 1 doggy!
julia> n = 2; println("I saw $n $(pluralize(n, "doggy", "doggies"))!")
I saw 2 doggies!
This will assume (by default) that pluralization is done by appending an "s", but allows for a custom plural to be optionally specified instead. If your use case is relatively simple, this may be sufficient.
If you need variable word selection but are willing to specify words with both their singular and plural forms, it’s a tad more involved but can still be done relatively simply. If that’s your regime, then we can offer suggestions to that effect.
Thanks @mikmoore . Yeah, I know I can put something together for this like the function you suggested, but I don’t love putting stuff like this into domain-specific code when it’s really orthogonal functionality that would live better somewhere else.
If there’s not something already out there, I’ll probably make it a local package and let it ferment for a while, then possibly release publicly at a later date.
I don’t know of such a library (EDIT: except for EnglishText.jl, at least I don’t know the best library or general for more languages), except in Python that you could use with PythonCall.jl directly (should be ok, since I doubt this is speed-critical, but either way, someone should make a Julia package, wrapping Python, to use it indirectly, then it could later be rewritten as pure Julia if speed-critical, to get rid of Python dependency):
It would be nice to have one, that not only supports English and exceptions like:
“VAX” => “VAXen”; # SINGULAR => PLURAL
E.g. for our Icelandic currency, króna (ISK), plural krónur:
1 króna
2 krónur
…
11 krónur
…
21 króna (singular word for used, also all nouns have 16 forms in Icelandic, 8 thereof singular; all adjectives have 120 forms)
…
101 króna …
EnglishText.jl indeed supports English and many common exceptions:
julia> pluralize("ox")
"oxen"
It currently pluralizes “krona” (which is usually written without accents in English) as “kronas” rather than “kronor” (the anglicized plural), but that rule could easily be added.
In the past day I’ve been reflecting on this and I think I may be attracted to the CLI library approach a little more than the Lingua::EN::Inflect approach. The big difference being whether the package itself knows about your language (English, in this case - not Julia) and how plurals are formed, or whether you supply that knowledge as the caller.
Specifically, it’s the difference between these two options (using a pseudo-syntax p"" I’m just making up):
p"There {verb:was} {n} {noun:apple} in the basket." (Inflect style)
p"There {?was,were} {n} apple{?s} in the basket." (CLI style)
In the first case, it “knows” how to conjugate the verb was and how to form the plural of apple if necessary.
In the second case, you manually tell it that info and it’s just providing the syntactic sugar to get the right strings selected.
It’s kind of interesting to me that often, the latter is a bit more concise and easy to read than the former, and it doesn’t have to be as smart either (and will therefore probably make fewer mistakes?).