Окончания числительных в php

Сегодня надо было решить одну задачку, а именно — вывести цифру и слово, у которого окончание зависит от числа. 5 Человек, 3 человека, 1928 человек и так далее. Самих чисел может быть сколько угодно.

Полез в нет за готовым решением, нашел там одни циклы по условию

if( тут всякие деления по модулю == столько то){
то вывести так}
if( тут еще как нибудь){
вывести так}

Или тоже самое, но вариант switch … case…

Крайне громоздко, особенно если требуется вывести строку только в одном месте.Ну и работает до определенного потолка — если число будет миллион, то надо либо внутри цикла циклом цифру для умножения увеличивать, либо прописывать с руки все варианты.

Еще есть вариант забить все данные в массив и там искать нужную пару значений:

$array = (
"1", "человек",
"2", "человека",
....
if(in_array(....

Проблемы те же, надо либо генерить все возможные варианты, либо какую то хитрую функцию писать, чтобы искать там.

Еще один знакомый прогер подсказал вариант с ngettext. Но там также надо колдовать с расчетами:

<?php
/**
 * Returns:
 *   0, if $n == 1, 21, 31, 41, ...
 *   1, if $n == 2..4, 22..24, 32..34, ...
 *   2, if $n == 5..20, 25..30, 35..40, ...
 */
function plural( $n )
{
    if ( $n % 10 == 1 && $n % 100 != 11 )
    {
        return 0;
    }

    if ( $n % 10 >= 2 && $n % 10 <= 4 && ( $n % 100 < 10 || $n % 100 >= 20 ) )
    {
        return 1;
    }

    return 2;
}

// usage
for ( $i = 0; $i < 100; $i++ )
{
    $x = plural( $i );

    printf(
        "%d тетрад%s<br/>\n", $i,
         ( 0 == $x ? 'ь' : ( 1 == $x ? 'и' : 'ей' ) )
    );
}
?>

Output:
0 тетрадей
1 тетрадь
2 тетради
3 тетради
4 тетради
5 тетрадей
6 тетрадей
7 тетрадей
8 тетрадей
9 тетрадей
10 тетрадей
...

Also here is short version:

<?php
$n = 17;
print ($n%10==1 && $n%100!=11 ? 0 : ($n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2));
?>

output: 2

Тут один фиг на 100 делится, большие числа ну никак не пролезут. Мой вариант, делает тоже самое, только без ограничения на число, вычисляет по последней цифре:

$result = '19321';
$mess = preg_replace("(2|3|4)","тетради", substr($result, -1));
((substr($mess, -1) * 1) > 1 OR (substr($mess, -1) * 1) == 0)  ? $mess = "тетрадей" : $mess = "тетрадь";
echo $result . ' ' .$mess;

В одну строку так и не сумел придумать ((
UPD А вот сумел

echo (( (substr($str, -1) * 1) 1)) ? "тетради" : ( (( (substr($str, -1) * 1) == 0) OR ( (substr($str, -1) * 1) > 4)) ? "тетрадей" : "тетрадь" );

И полезный линк на тернарный оператор

3.75 avg. rating (76% score) - 4 votes

27 комментариев

  • Я, не стал писать свой велосипед, использую такой вариант http://cshrc.blog.ru/92267443.html Оригинал был размещен на Хабре, но пост не нашел.
     

    • Neolot, я просто хотел найти решение в одну строку, а тут вариант с предварительным заданием массива.
      ПыСы, прочитал твой комент и придумал, как сделать в одну строку 🙂
      $str = "329"; // tetradey
      echo (( (substr($str, -1) * 1) 1)) ? "тетради" : ( (( (substr($str, -1) * 1) == 0) OR ( (substr($str, -1) * 1) > 4)) ? "тетрадей" : "тетрадь" );

      Выглядит крайне ужасно ))
      Более понятно, когда выносим сравниваемую строку отдельно
      $str = "325"; // tetradey
      $a = (substr($str, -1) * 1);
      $a = (($a 1)) ? "тетради" : ( (($a == 0) OR ($a > 4)) ? "тетрадей" : "тетрадь" );
      echo $a . "\r\n";

      И все равно это не идеальный вариант потому, что 11 тетрадей но 21, 31, 41 тетрадь. Ну тут только исключение добавлять.

  • а зачем в одну строку ?:
    Краткость сестра таланта ?

    • Ну да ))

  • Да, решение надо использовать, а то всегда смешно, когда в каких нибудь сервисах пишут вам 24 «лет»:)

  • спасибо за подсказку

  • есть же хорошие плагины для wordpress, чем вам не нравиться?

  • Да, без исключений не обойтись, таков русский язык. Это еще хорошо получилось, в три строки.

  • Поддерживаю Yaroslavgrust для вордпрес сейчас же есть плагины зачем что то самому изобретать, если можно просто поискать в буржунете и потом останетса только русифицыровать что есть гораздо легче и быстрее

  • Видел некоторые плагины на wordpress полезные например и сохранении вашего времени.

  • Да, но кто говорит, чтоб использовать код в WordPress, не все же на этом движке написано.

  • Здорово ) я обычно пользуюсь функцией, которую написал давным-давно, предварительно проштудировав учебник русского ) но в одну строчку все то же самое — это круто!

  • О да! Нашел таки решение. Еле выцарапал из Гоши ваш пост… Спасибо, помогли ломаку в пхп 🙂

  • как то сталкивался с подобной задачей, но не смог найти рационального решения! спасибо за статью!

  • Трындец, как же это все сложно…

  • Да уж, в английском языке всё гораздо проще)

  •  
    Всегда юзал самописное. Юзаю в «количестве найдных документов» (найдено 10 документов).
     
    function inclination($time,$arr)
    {
    $timex = substr($time, -1);
    if ($time >=10 AND $time <=20) return $arr[2];
    elseif ($timex == 1) return $arr[0];
    elseif ($timex>1 AND $timex<5) return $arr[1];
    else return $arr[2];
    }
     

  • Забыл указать пример:)
    $x = 5;
    print inclination($x,array(‘лошадь’,’лошади’,’лошадей’)); //возвратит «лошадей»
     
     

  • Вот это тема!!! 🙂 Надо было её раньше найти.

    Я бился над этой задачей в июле этого (2011) года.

    Написал сначала один вариант… но он меня совершенно не устроил, тогда написал следующую функцию:

    // номер, если 0, если 1, от 2 до 4 включ, от 5 до 9, от 11 до 14, неизвестное значение
    function word_number($num, $word0, $word1, $word2_4, $word5_9, $word11_14, $word_n)
    {
    $order_1=$num-floor($num/10)*10;
    $order_2=$num-floor($num/100)*100;

    if($order_2>=11 && $order_2=2 && $order_1=5 && $order_1<=9) return ($word5_9);
    else return ($word);
    }

    И такой вызов:

    $txt_users=word_number($users,'участников','участник','участника','участников','участников','users');

    Если $users=3, то присвоит "участника".

  • В одну строчку
    declensions($users, array(«участник»,»участника»,»участников»));

    function declensions($count, $variants)
    {
    return ($count > 10 && $count 1 AND $count%10 < 5) ? $variants[1] : $variants[2] ) );
    }

  • Обрезалось прошлое сообщение

    function declensions($count, $variants)
    {
    return ($count > 10 && $count 1 AND $count%10 < 5) ? $variants[1] :
    $variants[2]));
    }

    • вариант с геттекстом более верен
      есть слова с двумя окончаниями, есть с четырьмя
      поэтому геттекст более кошерен в этом плане

  • Alex, мою функцию здорово порезало 🙁
    Мож сделаешь нормальное комметирование? Желательно с тегами, чтоб код можно было показать красиво.

    А сама функция такая, кстати:
    function word_number($num, $word0, $word1, $word2_4, $word5_9, $word11_14, $word_n){
    $order_1=$num-floor($num/10)*10;
    $order_2=$num-floor($num/100)*100;
    if($order_2>=11 && $order_2=2 && $order_1=5 && $order_1<=9) return ($word5_9);
    else return ($word);
    }

    Мож щас-то не порежет двиг блога… 🙂

  • Блин всё равно резануло(((

    Моя функция работает норм с любыми числами и нормальными существительными.
    По ссылке моего ника из этого сообщения пройти если, то можно увидеть работу функции в деле.
    Там слева в сайдбаре, есть список популярных форумов. И в этом списке указано кол-во «участников», «тем» и «сообщений» на каждом форуме. Вот эти существительные указываются в тексте соответственно числительным.

  • Интересный сайтег), но хочется видеть читаемый код, именно в постах в блоге.
    Alex, прикрутил бы приблуду-плагин для разметки и раскраски кода — было бы супер.

    А пока я занят созданием скрипта для тысяч чатов 🙂 типо специализированного _http://cbox.ws
    Но как появится свободное время, посмотрю другие записи блога 😉

  • Проще вот так: колдуем над словом РЕЦЕПТ

    function getTermRecipes ($num)
    {
    $number = substr($num, -1);

    if($number == 0) {$term = «ов»;}
    if($number == 1 ) {$term = «»;}
    if($number > 1 ) {$term = «а»;}
    if($number > 4 ) {$term = «ов»;}

    return $num.’ рецепт’.$term;
    }

    А подробнее можно посмотреть в этой статье: http://www.makannikov.ru/blog.php?id=19

css.php