function switching between singular and plural?
Asked Answered
A

12

27

I'm looking for a function that given a string it switches the string to singular/plural. I need it to work for european languages other than English.

Are there any functions that can do the trick? (Given a string to convert and the language?)

Thanks

Amoreta answered 18/1, 2011 at 20:58 Comment(8)
What other European language?Anthony
Here ya go: $result = (preg_match('~s$~i', $string) > 0) ? rtrim($string, 's') : sprintf('%ss', $string);. :PWolfgang
The trick with many languages is that a plural isn't just made on the noun, like how English adds an 's', the article and adjectives are also pluralized. Given the difficulties in parsing natural language to associate nouns with the correct modifiers, most software that I'm aware of just stores manual translations of both plural and non-plural versions.Cameral
@Alix Axel: ... fails for things like box, loss and many more ;)Dorothy
@nico: I was being ironic. =P This clearly depends on the language, and the OP didn't specify any...Wolfgang
This one is for the English laguage, but it might give you some inspiration: github.com/flourishlib/flourish-classes/blob/master/…Centerboard
for the record, not all forms of plural end in 's'Foregone
@AlixAxel: "depends on the language, and the OP didn't specify any..." -- I know it's old, but just FTR: there was no need to be ironic, since OP did specify clearly that the language should be an input of the function s/he was looking for. (E.g. as any locale-aware L10/I18N APIs work, i.e. the same way irrespective of the specific language.)Enwreathe
S
10

There is no function built into PHP for this type of operation. There are, however, some tools that you may be able to use to help accomplish your goal.

There is an unofficial Google Dictionary API which contains information on plurals. You can read more about that method here. You'll need to parse a JSON object to find the appropriate items and this method tends to be a little bit slow.

The other method that comes to mind is to use aspell, or one of its relatives. I'm not sure how accurate the dictionaries are for languages other than English but I've used aspell to expand words into their various forms.

I know this is not the most helpful answer, but hopefully it gives you a general direction to search in.

Sachsse answered 18/1, 2011 at 21:32 Comment(1)
The Google API does not seem to be active any more.Emaciation
M
50

Here is my handy function:

function plural( $amount, $singular = '', $plural = 's' ) {
    if ( $amount === 1 ) {
        return $singular;
    }
    return $plural;
}

By default, it just adds the 's' after the string. For example:

echo $posts . ' post' . plural( $posts );

This will echo '0 posts', '1 post', '2 posts', '3 posts', etc. But you can also do:

echo $replies . ' repl' . plural( $replies, 'y', 'ies' );

Which will echo '0 replies', '1 reply', '2 replies', '3 replies', etc. Or alternatively:

echo $replies . ' ' . plural( $replies, 'reply', 'replies' );

And it works for some other languages too. For example, in Spanish I do:

echo $comentarios . ' comentario' . plural( $comentarios );

Which will echo '0 comentarios', '1 comentario', '2 comentarios', '3 comentarios', etc. Or if adding an 's' is not the way, then:

echo $canciones . ' canci' . plural( $canciones, 'ón', 'ones' );

Which will echo '0 canciones', '1 canción', '2 canciones', '3 canciones', etc.

Mccarron answered 18/7, 2012 at 17:23 Comment(2)
This is really nice. More compact version: function plural( $amount, $singular = '', $plural = 's' ) { return ( $amount == 1 ) ? $singular : $plural; }Affettuoso
This doesn't work with dynamic values. What happens if the singular/plural word is not known?Forwhy
T
10

This is not easy: each language has its own rules for forming plurals of nouns. In English it tends to be that you put "-s" on the end of a word, unless it ends in "-x", "-s", "-z", "-sh", "-ch" in which case you add "-es". But then there's "mouse"=>"mice", "sheep"=>"sheep" etc.

The first thing to do, then, is to find out what the rule(s) are for forming the plural from the singular noun in the language(s) you want to work with. But that's not the whole solution. Another problem is recognising nouns. If you are given a noun, and need to convert it from singular to plural that's not too hard, but if you are given a piece of free text and you have to find the singular nouns and convert them to plural, that's a lot harder. You can get lists of nouns, but of course some words can be nouns and verbs ("hunt", "fish", "break" etc.) so then you need to parse the text to identify the nouns.

It's a big problem. There's probably an AI system out there that would do what you need, but I don't imagine there'll be anything free that does it all.

Turnery answered 18/1, 2011 at 21:23 Comment(6)
Another problem is that in some European languages nouns can be masculine and feminine, and the rules for pluralising them change depending on their gender. And to make things more complicated, there is no rule to determine whether a word is masculine or feminine, you'll have to look it up in a dictionary.Dorothy
well googling it there are a coulpe of function that does the trick in php, the problem is they work only for english. I need it for other european language.Amoreta
@yes123: I coded one for Portuguese, it correctly handles feminine / masculine inflations.Wolfgang
@Alix Axel: I don't know Portuguese, is there a clear rule to determine if a word is masculine or feminine?Dorothy
@nico: A clear rule? I wouldn't say so, no. I had to buy a "prontuário" (translation "chart" - more or less a dictionary for grammar rules) to discover that some pluralizations depend on the type of closing diphthongs. My implementation isn't perfect - I only managed to inflate most of the words, not all words - that would be an extremely hard task.Wolfgang
@Alix Axel: OK, I was just wondering because I tried to do it for Italian a while ago and I found it quite hard.Dorothy
S
10

There is no function built into PHP for this type of operation. There are, however, some tools that you may be able to use to help accomplish your goal.

There is an unofficial Google Dictionary API which contains information on plurals. You can read more about that method here. You'll need to parse a JSON object to find the appropriate items and this method tends to be a little bit slow.

The other method that comes to mind is to use aspell, or one of its relatives. I'm not sure how accurate the dictionaries are for languages other than English but I've used aspell to expand words into their various forms.

I know this is not the most helpful answer, but hopefully it gives you a general direction to search in.

Sachsse answered 18/1, 2011 at 21:32 Comment(1)
The Google API does not seem to be active any more.Emaciation
T
10

For anyone still stumbling across this in 2021, I suggest using the Inflector package from Doctrine.

Doctrine Inflector is a small library that can perform string manipulations with regard to uppercase/lowercase and singular/plural forms of words.

At time of writing, it supports English, French, Norwegian (bokmal), Portuguese, Spanish and Turkish.

Simply install it using composer: composer require doctrine/inflector and use it like this:

<?php
// Composer autoloading (your project probably already does this)
require __DIR__ . '/vendor/autoload.php';

// Build the inflector:
$inflector = \Doctrine\Inflector\InflectorFactory::create()->build();

// Singularize a word:
$inflector->singularize('houses'); // house
$inflector->singularize('vertices');  // vertex
Tades answered 30/7, 2021 at 9:24 Comment(0)
C
7

English Only Solution

Here is a PHP class that has worked flawlessly for me thus far: http://kuwamoto.org/2007/12/17/improved-pluralizing-in-php-actionscript-and-ror/

Here it is as a Gist in case that site goes away: https://gist.github.com/tbrianjones/ba0460cc1d55f357e00b

This is a small class and could easily and quickly be converted to other languages.

Calcicole answered 29/5, 2014 at 20:56 Comment(1)
This returns Stau as singular for Statuses and Adres for singular for AddressesOsterman
L
4

Here is a quick function that I usually use for less complex scenarios:

public static function getPluralPrase($phrase,$value){
    $plural='';
    if($value>1){
        for($i=0;$i<strlen($phrase);$i++){
            if($i==strlen($phrase)-1){
                $plural.=($phrase[$i]=='y')? 'ies':(($phrase[$i]=='s'|| $phrase[$i]=='x' || $phrase[$i]=='z' || $phrase[$i]=='ch' || $phrase[$i]=='sh')? $phrase[$i].'es' :$phrase[$i].'s');
            }else{
                $plural.=$phrase[$i];
            }
        }
        return $plural;
    }
    return $phrase;
}

Although for a more complex solution you can go with @T. Brian Jones' Solution

Lendlease answered 13/12, 2015 at 18:18 Comment(0)
C
2

I like the answer of James Constantino. Simple and efficient, and you're sure to master every case, in any language. Though one must write more code (twice the common part of the word, namely).

I also like, of course, the TBrianJones solution, but it works only with English (as is. I can easily enhance it for my usual European languages, German, French, Spanish, Italian and Portuguese). Thanks for it. If you know the frequency of the words that will be used, you can optimize it by reorganizing the lines.

Also Felipe's proposal is good, and btw plebiscited.

But in every solution, I found what I consider as a bug: each of you use the singular form only for the value 1, and the plural form for every other case. Which is obviously wrong for negative values, but also --at least in French-- for values strictly lower than 2.

So here is my proposal, taking James's code as the base, optimized:

function singleOrPlural($amount, $singular, $plural) {
  return (abs($amount)>=2
    ? $amount.' '.$plural 
    : $amount.' '.$singular
  );
}

So a code like

echo "We bought ".singleOrPlural(1.5, "pound", "pounds")
    ." of apples and ".singleOrPlural(2, "pound", "pounds")." of bread.";

will give

We bought 1.5 pound of apples and 2 pounds of bread.

which is correct.

For the zero value, I cannot understand the reason of a plural, since there is none of the object, but maybe anglosaxon natives are used with that.

HTH, M.

Condemnation answered 5/2, 2016 at 11:32 Comment(1)
I don't know if I agree that 1.5 pound of apples is "correct". That's not how I use English. I would speak that number as "one and a half" or "one point five"; either way, I would be saying "pounds" (because it is more than one).Accrete
I
1

I came up with a rather easy solution for something like this. However, this depends on some prior knowledge of general PHP as well as a custom mySQLi class, that can be found here (which explains my use of num_rows.

In my case, I needed to pull up a number of notes from a database and say "1 note" and then the word "notes" for everything after 1, as well as something to say "0 notes".

To pull from the database and do a count, I had to use: $total_notes = $database->num_rows("SELECT notes_id FROM $notes_db");

And then the code to echo the value...

<?php
 if($total_notes === 1){
 echo '<strong>1</strong> note.';
 }elseif($total_notes > 1){
 echo '<strong>'.$total_notes.'</strong> notes.';                                                                           
 }elseif($total_notes === 0){
 echo '<strong>0</strong> notes.';                                                                      
 }
?>
Insuppressible answered 18/1, 2018 at 22:21 Comment(0)
P
0

I have a multilanguage solution.

 function howmany($number,$singular,$plural) {
      if($number==1) return $number.' '.$singular;
      else return $number.' '.$plural;
 }

Call the function like so:

echo howmany($variablenumber,upcase('kaibigan'),upcase('ng kaibigan'));

Hope this helps!

Permissive answered 8/11, 2014 at 0:55 Comment(3)
If you view my solution as pragmatic rather than pedantic, it solves ALL multilanguage problems with singular and plural by relying on the programmer to supply the correct singular and plural forms, such as in english '1 property' and '2 properties', a use case for a web page listing real estate portfolios I have coded. Therefore, as a pragmatic solution, LIGHTWEIGHT, bug-free, and easy to implement, I would personally rate it as the most useful solution, if not the most pedantically correct solution. Cheers!Permissive
Your condition is not correct for many languages) Russian, for example, uses third form (genitive) for 5-19 :) [1 сайт, 2 сайта, 5 сайтов]Chequered
Aside from many languages have far more complex plural rules, this code ironically also doesn't work for Filipino, as in your example...Launceston
M
0

You can try the following,

if ( !function_exists('pluralize') ) {
    function pluralize( $amount, $singular, $plural, $show=false ) {
        $OP = $show ? $amount.' ' : '';
        $OP .= ( $amount == 1 ) ? $singular : $plural;
        return $OP;
    }
}
Mathis answered 23/3, 2016 at 20:23 Comment(0)
I
0

I used user28864's idea but then rewrote it:

class StringUtil{
/**
 * Returns the English plural of the given phrase based on the value.
 *
 * @see https://mcmap.net/q/495512/-function-switching-between-singular-and-plural (idea from this)
 * @param string $phrase The phrase to determine the plural form
 * @param float|int|array|object $value The value to determine whether the phrase should be plural or not
 * @return string The English plural form of the phrase based on the value
 */
public static function pluralEnglish(string $phrase, float|int|array|object $value): string{
    if (empty($phrase) || is_numeric($phrase)){
        return $phrase;
    }

    if (is_object($value) || is_array($value)){
        $val = count($value);
    }else{
        $val = $value;
    }
    if ($val == 1){
        return $phrase;
    }

    //since the value is not singular, it is plural, so we need to come up with the english plural.
    $last = substr($phrase, -1);
    $last2 = substr($phrase, -2);
    $replace = false;
    $append = false;
    switch($last){
        case 'x':
        case 'z':
        case 's':
            $append = 'es';
            break;
        case 'y':
            $replace = 'ies';
            break;
        default:
            switch($last2){
                case 'ch':
                case 'sh':
                    $append = 'es';
                    break;
                default:
                    $append = 's';
            }
    }
    if ($append) {
        return $phrase . $append;
    }else if ($replace){
        return substr($phrase, 0, -1) . $replace;
    }else{
        return $phrase;
    }
}
}

And you can run some tests like this:

$ary = [
'kibitz',
'miss',
'fox',
'box',
'item',
'god',
'fun',
'diety',
'touch',
'teach',
'watch',
'test',
'fire',
];
$count = 0;
foreach($ary as $phrase){
   echo StringUtil::pluralEnglish($phrase, $count)."\n";
}

I made a 3v4l here: https://3v4l.org/VJni0

Inpour answered 15/2 at 18:12 Comment(4)
I have a few issues such as: 'appendix, axis, fungus, deer, child, syllabus, louse, person, goose, bacterium, and many more. Also else if should be one word in PHP.Accrete
Yes there are exceptions or whitelists if you will, but this does a good job of most cases. Why do you think it should be "elseif" "else if" is perfectly valid.Inpour
else if does not comply with PSR-12 coding standards. elseif is a single nominated keyword in PHP.Accrete
Ok thank you for that line of reasoning, I never understood why people use this or that if they do they same thing, thought it was just a preference.Inpour
I
0

I refactored my original answer to be what I think is better: See 3v4l: https://3v4l.org/3eSF4 or below

class StringUtil{ 
/**
 * Returns the English plural of the given phrase based on the value.
 * Note, there are certainly exceptions and if someone wants to white/black-list them, go ahead.
 * Some exceptions that do not work with this function noted by mickmackusa: 
 *  appendix, axis, fungus, deer, child, syllabus, louse, person, goose, bacterium
 * @see first version: https://3v4l.org/VJni0
 * @see https://mcmap.net/q/495512/-function-switching-between-singular-and-plural (idea from this)
 * @param string $phrase The phrase to determine the plural form
 * @param float|int|array|object $value The value to determine whether the phrase should be plural or not
 * @return string The English plural form of the phrase based on the value
 */     
 public static function pluralEnglish(string $singularPhrase, float|int|array|object $value): string{
    if ($value === 1){
        return $singularPhrase;
    }
    if (is_countable($value)){
        if (count($value) === 1){
            return $singularPhrase;
        }
    }else if (is_object($value)){
        //object that does not implement Countable
        return $singularPhrase;
    }

    //If the string doesn't even end with a letter, just return the phrase.
    if (!preg_match('/[a-zA-Z]$/', $singularPhrase)){
        return $singularPhrase;
    }

    if (str_ends_with($singularPhrase, 'y')){
        return substr($singularPhrase, 0, -1).'ies';
    }

    //In English, words that end with these characters, you add "es" to make them plural.
    $es_append = ['x','z','s','ch','sh'];
    foreach($es_append as $end){
        if (str_ends_with($singularPhrase, $end)){
            return $singularPhrase.'es';
        }
    }
    //It didn't fit any of the above criteria, so just add "s"
    return $singularPhrase.'s';
}

} }

Inpour answered 21/2 at 21:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.