##==============================================================================
## Text::Pluralize - simple pluralization routine
##==============================================================================
## Copyright 2004 Kevin Michael Vail
## This program is free software; you can redistribute it and/or modify it under
## the same terms as Perl itself.
##==============================================================================
## $Id: Pluralize.pm,v 1.0 2004/05/23 05:33:14 kevin Exp $
##==============================================================================
require 5.006;
package Text::Pluralize;
use strict;
use warnings;
our ($VERSION) = q$Revision: 1.0 $ =~ /^Revision:\s+(\S+)/ or $VERSION = "0.0";
use base qw(Exporter);
our @EXPORT = qw/pluralize/;
=head1 NAME
Text::Pluralize - simple pluralization routine
=head1 SYNOPSIS
use Text::Pluralize;
print pluralize("file", $count);
print pluralize("%d file(s) copied\n"), $count;
print pluralize("There (was|were) {no|one|%d} error(s)\n", $count);
=head1 DESCRIPTION
Text::Pluralize provides a lightweight routine to produce the proper form,
singular or plural, of a word or phrase. Its intended purpose is to produce
messages for the user, whether error messages or informational messages, without
the awkward "1 file(s) copied" appearance.
=head1 EXPORTED ROUTINE
=over 4
=item I<$string> = pluralize(I<$template>, I<$count>);
Returns I<$template> customized by I<$count>. I<$template> may contain items
matching the following formats:
=over 4
=item C<< (I<s1>|I<pl>) >>
If I<$count> is equal to one, I<s1> will appear here; otherwise I<pl> will
appear at this point in the output. Either I<s1> or I<pl> can be empty.
=item C<< (I<pl>) >>
If I<$count> is not equal to one, the string I<pl> will appear at this point in
the output. This is equivalent to C<< (|I<pl>) >>.
=item C<< (I<s1>|I<s2>|...|I<pl>) >>
This can be generalized. I<s1> is used if I<$count> is equal to one, I<s2> if
the count is equal to two, and so forth; I<pl> is used for anything greater than
the last specific string applied.
=item C<< {I<s0>|I<s1>|I<pl>} >>
With curly braces, the choices start at zero. I<s0> is used if I<$count> is
zero, I<s1> if it's one, and I<pl> if it's anything else.
=item C<< {I<s0>|I<s1>|I<s2>|...|I<pl>} >>
As with the parenthesized version, this can be generalized.
=back
If none of the above substitutions appear in I<$template>, it is treated as if
it ended in C<< (s) >>.
Once the above substitutions have been applied, the result is examined to see if
it contains any C<%> characters. If so, it is used as a format for
L<sprintf|perlfunc/sprintf>, with the count and any other arguments passed to
B<pluralize>. This means that if you have a C<%> in your template that is I<not>
supposed to be a format character, you must specify C<%%> instead.
=back
=head1 EXAMPLES
In each of the examples below, the first column represents the template, the
second column the count, and the third column the result.
item 0 items
1 item
2 items
item(s) need{|s|} attention 0 items need attention
1 item needs attention
2 items need attention
{No|%d} quer(y|ies) (is|are) 0 No queries are
1 1 query is
2 2 queries are
{No|One|Two|Three|%d} item(s) 0 No items
1 One item
2 Two items
3 Three items
4 4 items
=head1 NOTE
If the brackets for a substitution don't match up, the one on the left controls
what happens.
=head1 COPYRIGHT AND LICENSE
Copyright 2004 Kevin Michael Vail
This program is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.
=head1 AUTHOR
Kevin Michael Vail <F<kevin>@F<vaildc>.F<net>>
=cut
##==============================================================================
## pluralize
##==============================================================================
sub pluralize ($$;@) {
my ($template, $count) = splice @_, 0, 2;
my $output = '';
my $control_count = 0;
while (
$template =~ /
^([^({]*) ## leading string up to a ( or {
((?: ## either
\([^|)}]*[)}] ## ( string )
| ## or
[({][^|]* ## ( or { up to the first |
(?:\|[^|)}]*)+ ## one or more | followed by non-|, ), or }
[)}] ## closing ) or }
))
(.*)$ ## and then the rest of the string
/x
) {
++$control_count;
$output .= $1;
$template = $3;
my $pattern = $2;
my @alternatives;
if ($pattern =~ /^\((.*)[)}]$/) {
@alternatives = split /\|/, $1;
push @alternatives, '' if $1 =~ /\|$/;
unshift @alternatives, '' if @alternatives == 1;
unshift @alternatives, $alternatives[-1];
} elsif ($pattern =~ /^\{(.*)[})]$/) {
@alternatives = split /\|/, $1;
push @alternatives, '' if $1 =~ /\|$/;
} else {
$output .= $pattern;
--$control_count;
next;
}
if ($count >= $#alternatives || $count < 0) {
$output .= $alternatives[-1];
} else {
$output .= $alternatives[$count];
}
}
$output .= $template;
if ($control_count == 0 && $count != 1) {
$output .= 's';
}
$output = sprintf $output, $count, @_ if $output =~ /%/;
return $output;
}
1;
##==============================================================================
## $Log: Pluralize.pm,v $
## Revision 1.0 2004/05/23 05:33:14 kevin
## Initial revision
##
##==============================================================================