Война с роботами: склонение существительных после числительных

Робот в кармашкеВ первой части Терминатора™ Рис Кайл рассказывает о том, как круто роботы научились маскироваться под людей. Что сейчас у них настоящие кожа и волосы, они потеют и т. п. Про предыдущие же модели он говорил, что их легко было отличить по резиновой коже.

Думаю, что ещё более ранние модели отличались совсем просто — они говорили: «Солдат Крис Катарн, убил 10 враг, потратил 342 патрон, получил 0 ранение» и т. п., чем сразу палились.

Ведь до сих пор, несмотря на развитие веба, на многих сайтах можно встретить «50 пользователи», «1 комментарии», «0 сообщения» и т. п. А ведь насколько приятней, когда сайт говорит с тобой на человеческом языке и правильно спрягает слова по числам.

И ведь сделать это совсем несложно. Ниже приведены простые готовые функции, которые позволяют решить эту проблему на PHP и Javascript. Они настолько просты, что не составит труда перенести их на любой другой язык.

В функцию передаётся число сущностей, для которого нужно подобрать окончания, и массив слов (или окончаний для слов) для чисел 1, 4 и 5. Например ['устрица', 'устрицы', 'устриц'].

PHP



  1. /**
  2.  * Функция возвращает окончание для множественного числа слова на основании числа и массива окончаний
  3.  * @param  $number Integer Число на основе которого нужно сформировать окончание
  4.  * @param  $endingsArray  Array Массив слов или окончаний для чисел (1, 4, 5),
  5.  *         например array('яблоко', 'яблока', 'яблок')
  6.  * @return String
  7.  */
  8. function getNumEnding($number, $endingArray)
  9. {
  10.     $number = $number % 100;
  11.     if ($number>=11 && $number<=19) {
  12.         $ending=$endingArray[2];
  13.     }
  14.     else {
  15.         $i = $number % 10;
  16.         switch ($i)
  17.         {
  18.             case (1): $ending = $endingArray[0]; break;
  19.             case (2):
  20.             case (3):
  21.             case (4): $ending = $endingArray[1]; break;
  22.             default: $ending=$endingArray[2];
  23.         }
  24.     }
  25.     return $ending;
  26. }


JavaScript


  1. /**
  2.  * Функция возвращает окончание для множественного числа слова на основании числа и массива окончаний
  3.  * @param  iNumber Integer Число на основе которого нужно сформировать окончание
  4.  * @param  aEndings Array Массив слов или окончаний для чисел (1, 4, 5),
  5.  *         например ['яблоко', 'яблока', 'яблок']
  6.  * @return String
  7.  */
  8. function getNumEnding(iNumber, aEndings)
  9. {
  10.     var sEnding, i;
  11.     iNumber = iNumber % 100;
  12.     if (iNumber>=11 && iNumber<=19) {
  13.         sEnding=aEndings[2];
  14.     }
  15.     else {
  16.         i = iNumber % 10;
  17.         switch (i)
  18.         {
  19.             case (1): sEnding = aEndings[0]; break;
  20.             case (2):
  21.             case (3):
  22.             case (4): sEnding = aEndings[1]; break;
  23.             default: sEnding = aEndings[2];
  24.         }
  25.     }
  26.     return sEnding;
  27. }


Не забудьте отдельно обработать случай, для числа 0. Просто написать, например, «0 записей» не достаточно. Нужно как минимум написать «Записей нет» или изменить дизайн, скрыв вообще пустой блок с записями.

UPD: Спасибо IGlukhovу за то, что поправил неграмотное название!
+31
8 октября 2010, 11:18
217
Adrior 94,9

комментарии (113)

+2
izorg #
У себя в проектах использую этот код, не знаю автора, к сожалению, но работает отлично.

/**
 * Возвращает единицу измерения с правильным окончанием
 * 
 * @param {Number} num      Число
 * @param {Object} cases    Варианты слова {nom: 'час', gen: 'часа', plu: 'часов'}
 * @return {String}            
 */
function units(num, cases) {
    num = Math.abs(num);
    
    var word = '';
    
    if (num.toString().indexOf('.') > -1) {
        word = cases.gen;
    } else { 
        word = (
            num % 10 == 1 && num % 100 != 11 
                ? cases.nom
                : num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) 
                    ? cases.gen
                    : cases.plu
        );
    }
    
    return word;
}
–1
AigizK #
проще можно:
/// /// по числу возврщает индекс из массива, чтоб было правильное окончание
/// массив должен быть такой ['просмотр', 'просмотра', 'просмотров']
///
///
///
public static int WorksEnd(int number)
{
if(Regex.Match(number.ToString(), «1\\d$»).Success)
return 2;
if(Regex.Match(number.ToString(), «1$»).Success)
return 0;
if(Regex.Match(number.ToString(), "(2|3|4)$").Success)
return 1;
return 2;
}
0
draf_grakula #
На всякий случай, может кому пригодится:
nom — Nominativ, именительный падеж;
gen — Genetiv, родительный падеж;
plu — Plural, множественное число.

+3
Barmaleikin #
Вот бы ещё додумались не писать бред типа 01 января.
0
sergeant #
Или даже «01 Января».
+1
br3t #
А вот хотелось бы, чтобы не забивать базы всякими яблоками, одну функцию на все слова, которая определив склонение, сама высчитывала нужное окончание… Даже несмотря на великость и могучесть великого и могучего точность такой функции, на первый взгляд, можно довести до 70%
0
Volshebnyi #
Для английского писал, причем поддерживала даже user's — users'.
Для русского из-за того, что окончание зависит от рода существительного (пользователь, канитель), не стал писать что-то серьезное.
0
soshial #
и ещё от одушевлённости, между прочим, тоже, так что нифига :)
0
Volshebnyi #
Нет, от одушевленности зависит только окончания при склонении. Видеть кого? Пользователя. Видеть что? Бордель.
Но 5 пользователей, 5 борделей.
Может, я о чем-то забыл, но вроде как на окончание во множественном числе одушевленность не влияет.
0
stanishevsky #
5 человек, 5 веков
+1
kmike #
в pymorphy это есть, для питона, правда)
0
br3t #
Поправьте меня, если я ошибаюсь, но там кажется задействованы словари, а хотелось как раз без этого
+3
kmike #
Конечно, словари. Как иначе «человек-люди» во множественное число поставишь. А для слов, которых нет в словаре, прекрасно работает предсказатель, который склоняет слова по аналогии. Что imho работает лучше и проще каких-то вручную прописанных правил.

Прописывание правил в коде в пределе как раз и приводит к составлению словаря (только в довольно корявом и неудобным для поддержки виде).
0
Volshebnyi #
Сейчас на сайте 10 человеков. Только что зашедшие человеки: Филипп, Туранга.
–1
Dragonizer #
Тогда уж, продолжая тему:
*Бендер заходит в чат
*Человеки покидает чат
–1
tenshi #
Сейчас на сайте 10 людей. Только что зашедшие люди: Филипп, Туранга.

один фиг
0
Nashev #
сейчас на сайте 1 людь

(извините, что отвечаю в старую тему)
+1
Adrior #
Есть и PHPMorphy, но там словари по 7 Мб и ресурсов он кушает прилично. А в большинстве задач заранее известно чего будет спрягаться. Так что не вижу особой выгоды в универсальной функции.
+2
kmike #
угу, морфология для этого — из пушки по воробьям. Разве что слова из базы обрабатывать.

введите название предмета: сепулька
у Вас 15 сепулек
+12
Krivoy #
— Дайте мне пять шаурмей, ммм… 5 шаурменей, 5 шаурмов…
— Дайте мне две шаурмы и три шаурмы.
+1
forgotten #
И пять кочерег!
+9
alexxz #
Весьма рекомендую использовать тщательно собранные формулы для разных языков translate.sourceforge.net/wiki/l10n/pluralforms

PHP-шники аккуратно, там формулы даны для С, а в С немного другие приоритеты операторов, потому придётся расставить скобочки.
–3
viperet #
Для тех, кто использует Smarty:
<?php
function smarty_function_ending($params, &$smarty)
{
$base = !isset($params['base']) ? '' : $params['base'];

$e0 = $params['e0'];
$e1 = $params['e1'];
$e2 = $params['e2'];
$number = $params['number'];

if ($number % 100 > 10 && $number % 100 < 20) return $base . $e0;
if ($number % 10 == 1) return $base . $e1;
if ($number % 10 >= 2 && $number % 10 <= 4) return $base . $e2;
return $base . $e0;
}


ложим в smarty/plugins/function.ending.php и используем
{$item.comments} {ending number=$item.comments base=«комментар» e0='иев' e1='ий' e2='ия'}
+15
sergeant #
Отлично, теперь мы будем ложить со склонениями!
+5
torbasow #
Кладём!
–1
varenov_nikita #
Я с вас фигею, ребят. Человек дает вам работающее программное решение и получает минусы. Ему отвечают банальным занудством и огребают кучу плюсов. Ну-ну… а мы-то и не знали, как правильно: лОжить или ложИть.
0
sergeant #
Ну-ну… Раз вы не знали, то правильно: класть.
0
tenshi #
как ты заверстаешь такие фразы?
* к комментарию не добавлено плюсов
* к комментарию добавлен плюс
* к комментарию добавлены 2 плюса
* к комментарию добавлены 5 плюсов

попутно… как правильно?
* к комментарию добавлен 1.1 плюс
* к комментарию добавлен 1.1 плюса
0
viperet #
Где то так:
к комментарию
{if $comments == 0}
не добавлено плюсов
{elseif $comments == 1}
добавлен плюс
{else}
добавлены {$comments} {ending number=$comments base="плюс" e0="ов" e1="" e2="а"}
{/if}


попутно — думаю что правильнее «к комментарию добавлен 1.1 плюса» — «одна целая одна десятая плюса», но приведенный мной код не работает правильно с дробными числами.
0
tenshi #
вот видишь, пришлось добавлять пачку исключений… а ведь куда приятней было бы так:

к комментарию {count $comments}не добавлено{/}добавлен{/}добавлены {$comments}{/count} {plural $comments}плюсов{/}плюс{/}плюса{/plural}

«одна целая одна десятая плюса» — так не говорят, говорят: «один и один плюс»
0
eyedmax #
уууу извращенец!
громоздковатая конструкция, не находишь?
для тех, кто использует смарти имхо проще сделать как то так:

<?php
function smarty_modifier_russify($i, $s1, $s4 = false, $s5 = false)
{
    if (false == $s4) $s4 = $s1;
    if (false == $s5) $s5 = $s1;
    return Funcs::getRussianNumEnding($i, array($s1, $s4, $s5));
}
?>

суём это дело в \smarty\plugins\modifier.russify.php и используем:

{$item.comments} комментар{$item.comments|russify:'иев':'ий':'ия'}

ну или там

Всего пользователей: {$usersTotal} {$usersTotal|russify:'человек':'человека'}
0
MegafonUral #
>массив слов (или окончаний для слов) для чисел 1, 4 и 5

проще запомнить как «один», «два», «много»
+7
Napolsky #
Вообще в такие функции обычно передаются окончания для чисел 0, 1 и 2 (согласитесь, это намного логичнее чем 1,4 и 5 или любой другой вариант).

И, надо сказать, я удивлен таким постом, рассказывающем абсолютно банальные вещи — не надо пожалуйста опускать планку статей ниже плинтуса. А то такими темпами скоро появятся статью «Условные операторы в PHP» или «Как включить компьютер»
+2
Napolsky #
Полезной информации в виде реализации в данном топике тоже не вижу — если уж программист не способен 1 раз в жизни написать для себя функцию в 10 строк, то гугл в помощь — на первых строках в выдаче есть реализации получше предложенной.
0
Adrior #
Можно было не приводить примеры функций, они и в самом деле элементарны. Тут скорее попытка привлечь внимание общественности к проблеме. Но выложить на Хабре статью и без примера — это как-то, по-моему, странно.
0
Napolsky #
Я понял. Но в итоге просто у вас получилась статья на 2 экрана, вся суть которой умещается в 1 предложение.
0
kirilloid #
Угу. Лучше бы раскрыли тему интеграции с gettext, в котором эти функции уже зашиты для кучи языков.
+7
DenisioDelBoro #
Пользователь elfiki когда-то написал лаконичную функцию на PHP, пост с которой почему-то быстро поместил в черновики. К счастью, я успел зазвездить пост в ридере.

/**
* @param $number int число чего-либо
* @param $titles array варинаты написания для количества 1, 2 и 5
* @return string
*/
function human_plural_form($number, $titles=array('комментарий','комментария','комментариев')){
$cases = array (2, 0, 1, 1, 1, 2);
return $number." ".$titles[ ($number%100>4 && $number%100<20)? 2: $cases[min($number%10, 5)] ];
}
0
apollonin #
Мои вариант. Вроде как короче выглядит.

//Для вывода статистики на екран.
//firststr — пользователь
//secondstr — пользователя
//thirdstr — пользователей
function get_name($count,$firststr,$secondstr,$thirdstr)
{
$ost = $count % 100;
if ($ost > 9 && $ost < 20)
{
return $thirdstr;
}
else
{
$ost = $ost % 10;
if ($ost == 1)
return $firststr;
elseif ($ost > 1 && $ost < 5)
return $secondstr;
else
return $thirdstr;
}
}
0
Arion #
Рад что перешел на Ruby on Rails и использую, сейчас уже встроенную, библиотеку i18n
+1
Setti #
gettext тоже умеет plurals
0
netrain #
$plural = $n%10==1&&$n%100!=11?'комментарий':($n%10>=2&&$n%10<=4&&($n%100<10||$n%100>=20)?'комментария':'комментариев');

я думал, алгоритм подстановки слова в нужной форме уже ни для кого не секрет :-)
+3
Napolsky #
Топик медленно перетекает в «Самые извращенские реализации функции окончания числительных» :)
+10
IGlukhov #
Это называется склонение существительных после числительных, а не «спряжение слов по числам».
0
tenshi #
а перед числительными их типа склонять не надо?
0
Indeego #
На php все гораздо проще:
0
Indeego #
сорри, вот:
    function declOfNum($number, $titles)  
    {  
        $cases = array (2, 0, 1, 1, 1, 2);  
        return $number." ".$titles[ ($number%100 > 4 && $number %100 < 20) ? 2 : $cases[min($number%10, 5)] ];  
    } 
0
majesty #
переписывается на JS за 1 минуту, не менее просто :)
в этом коде нет ни одного выражения php, которое отсутствует в Javascript :)

зы: не могу утверждать, но мне кажется, что автор этого кода мой друг (не уверен, потому что он мог его где-то вычитать) — forum.woweb.ru/topic28561.html
НЛО прилетело и опубликовало эту надпись здесь
+1
br3t #
Не будет бесполезным добавить функции 3ий параметр, отвечающий за обработку нулевого значения, ср.
В теме 0 комментариев
До события осталось нет часов

З.Ы. Это не просьба о помощи, а «хозяйке на заметку»
+1
i360u #
А можно так:
Сообщения: 12
Устрицы: 76
и т.д. Ведь сайт это все-таки не человек а интерфейс, а в интерфейсах такое вполне допустимо.
+2
Bonch #
Rails (gem russian):

Russian::pluralize
Russian::p

Russian.p(1, "вещь", "вещи", "вещей")
=> "вещь"
Russian.p(2, "вещь", "вещи", "вещей")
=> "вещи"
Russian.p(10, "вещь", "вещи", "вещей")
=> "вещей"
Russian.p(3.14, "вещь", "вещи", "вещей", "вещи") # последний вариант используется для дробных величин
=> "вещи"
+1
furyk #
кстати, частенько стоящее перед числительным слово тоже меняется.

например:

остался 1 час
осталось 2 часа

так что для полноценной работы функцию стоит дописать.

свою я выкладывать не буду, она страшная и уродливая.
+4
KeepYourMind #
Можно использовать gettext.
0
qmax #
а как там реализован универсальный (для любых языков) способ склонения?
0
KeepYourMind #
Насколько я знаю, да.
0
Nashev #
Извините, но запятой после слова «как» не было, так что вопрос был в нём.
+3
maxpain #
Для XML/XSL.
Функция склонения
<!-- Склонение после числительных -->
<xsl:template name="declension">
	<!-- Число -->
	<xsl:param name="number" select="number"/>
	
	<!-- "Комментарий" -->
	<xsl:param name="f0" select="f0" />

	<!-- "Комментария" -->
	<xsl:param name="f1" select="f1" />

	<!-- "Комментариев" -->
	<xsl:param name="f2" select="f2" />
	<xsl:variable name="absnum">
		<xsl:choose>
			<xsl:when test="$number < 0"><xsl:value-of select="0 - $number" /></xsl:when>
			<xsl:otherwise><xsl:value-of select="$number" /></xsl:otherwise>
		</xsl:choose>
	</xsl:variable>
	
	<xsl:choose>
	   <xsl:when test="($absnum mod 10) = 1 and ($absnum mod 100) != 11">
		  <xsl:value-of select="$f0"/>
	   </xsl:when>
	   <xsl:when test="(($absnum mod 10) >= 2) and (($absnum mod 10) <= 4) and (($absnum mod 100 < 10) or ($absnum mod 100 >= 20))">
		  <xsl:value-of select="$f1"/>
	   </xsl:when>
	   <xsl:otherwise>
		  <xsl:value-of select="$f2"/>
	   </xsl:otherwise>
	</xsl:choose>
</xsl:template>

Вызов
<xsl:call-template name="declension">
	<xsl:with-param name="number" select="value-of-your-counter-or-number" />
	
	<xsl:with-param name="f0" select="'комментарий'" />
	<xsl:with-param name="f1" select="'комментария'" />
	<xsl:with-param name="f2" select="'комментариев'" />
</xsl:call-template>

Можно легко усовершенствовать — я подгружал функцией document() XML-словарь по коду языка (en.xml, ru.xml и т.д.), взятому из преобразуемого XML-файла (скажем в root-элементе тег lang с нужным значением) и модифицировал функцию declension для работы с разными языками, передавая не непосредственно слова в 3-х формах, а id фразы/слова из словаря.
+1
maxpain #
Хабрапарсер преобразовал символы… внутри всех параметров test вместо < и > должны быть &gt; и &lt;, т.е. <= и >= надо писать как как &gt;= и &lt;= соответственно.
0
rednaxi #
мы используем похожий шаблон, только с 2 параметрами — первым передаем число, а вторым формы слова через запятую — мне кажется так удобнее чем делать 4 параметра :)
–1
Pilot34 #
Обалдеть) Буквально 15 минут назад на работе написал такое на C# для количества дней))) Жестоко совпало)

private static string GetDaysWithCase(int daysCount)
{
if (daysCount < 0)
throw new ArgumentException("daysCount");

string days = daysCount.ToString();
string daysCase = "дней";
char lastChar = days[days.Length - 1];

if (days.Length >= 2 && days[days.Length - 2] == '1')
{
// 10-19 дней
}
else if (lastChar == '1')
{
// 1, 21, 31 день
daysCase = "день";
}
else if (lastChar == '2' || lastChar == '3' || lastChar == '4')
{
// 2, 3, 4, 22, 33, 44 дня
daysCase = "дня";
}

return string.Format("{0} {1}", days, daysCase);
}
0
Pilot34 #
а подсветка-то и не работает :(
private static string GetDaysWithCase(int daysCount)
    {
      if (daysCount < 0)
        throw new ArgumentException("daysCount");

      string days = daysCount.ToString();
      string daysCase = "дней";
      char lastChar = days[days.Length - 1];

      if (days.Length >= 2 && days[days.Length - 2] == '1')
      {
        // 10-19 дней
      }
      else if (lastChar == '1')
      {
        // 1, 21, 31 день
        daysCase = "день";
      }
      else if (lastChar == '2' || lastChar == '3' || lastChar == '4')
      {
        // 2, 3, 4, 22, 33, 44 дня
        daysCase = "дня";
      }
      
      return string.Format("{0} {1}", days, daysCase);
    }


* This source code was highlighted with Source Code Highlighter.
+1
nikelin #
function declOfNum( $number, $titles ) {
$cases = array( 2, 0, 1, 1, 1, 2 );
return $titles[ ( $number % 100 > 4 && $number % 100 < 20 ) ? 2 : $cases[ mi
n( $number % 10, 5 ) ] ];
}

+13
docomo #
И это на главной Хабра?! :(
+6
kost #
И не в первый раз.
+3
braintorch #
Не забудьте отдельно обработать случай, для числа 0. Просто написать, например, «0 записей» не достаточно. Нужно как минимум написать «Записей нет» или изменить дизайн, скрыв вообще пустой блок с записями.


Вот удивляет меня эта тяга к максимальному очеловечиванию интерфейса в ущерб удобству. Когда я ищу взглядом на странице количество записей (комментариев, еще чего-нибудь), я ищу число. Числа выделяются посреди текста, по ним можно ориентироваться. Заменяя число текстом, меня лишают этой возможности. Даже когда я привыкну к интерфейсу и буду знать, в каком именно месте отображается количество записей, я всё равно буду спотыкаться взглядом об это «Записей нет», ожидая увидеть цифру на месте количества.

Скрывать пустой блок с записями — тоже не самое разумное решение. Есть возможность, что я буду долго блуждать по странице, пытаясь понять «где же, блин, тут записи должны отображаться». А когда запись появится, меня неприятно удивит блок, внезапно вылезший неизвестно откуда и нарушивший картину, к которой я только успел привыкнуть.
0
burjui #
Простым пользователям, считающим компьютеры магией, проще понять, что «чего-то нет», чем понять «обнаружено 0 чего-то». Почитайте разговоры этих самых пользователей с техподдержкой (:
Я за разделение интерфейсов для «крутых» и обычных пользователей. Крутым — «0 записей в базе», обычным — «ничего не найдено». По умолчанию интерфейс делать «обычным», в настройки кинуть галочку — «интерфейс для опытных пользователей» или «расширенный интерфейс».
0
tenshi #
где почитать? они прямо так и пишут «у вас написано что найдено 0 записей, но ни одной записи ведь не найдено?!?! а-а-а, у меня кипит мозг!»?

ноль проходят в начальной школе. ЛЮБОЙ человек его понимает. даже совсем отсталый. а возмущает это лишь числоненавистников.
0
klubben #
Давно пользуюсь вот этой функцией luchinsky.ru/coding/lexica/
Написана для php и легко портируется на js
0
TiGR #
Неплохо бы в такие функции добавлять опциональный вариант на случай, если число — 0. Чтобы не писать «0 ответов», а «Ответов нет».
0
stanishevsky #
Ответы есть, и их 12 :)
0
netrain #
Накатал за пару часиков. Правила конечно же не все заложил, да и это работа не одного дня. Но все же: dvx-dev.ru/plural.php
Вводим слово в именительном падеже (в ед. числе, разумеется). Можно добавить прилагательное впереди.
0
dfuse #
forum.dklab.ru/viewtopic.php?t=6760 заметьте, теме уже 7 лет как…
+7
muromec #
php-ника хлебом не корми, дай написать велосипед.

а ведь еще в двадцатом веке люди придумали gettext
0
tenshi #
на геттексте свет клином не сошёлся. тем более что ngettext не поддерживает русский язык.
0
muromec #
>ngettext не поддерживает русский язык.

в php какой-то особый ngettext, в котором традиционно ничего не работает?
0
tenshi #
самый обычный. в качестве параметров она везде принимает лишь 2 формы. писать на английском языке приложение рассчитанное прежде всего на русскоязычную аудиторию, а потом отдавать переводчикам переводить обратно на русский — весьма глупо.
0
muromec #
>писать на английском языке приложение рассчитанное прежде всего на русскоязычную аудиторию, а потом отдавать переводчикам переводить обратно на русский — весьма глупо.

это проблемы вашего техпроцесса, а не функции gettext.

можно задать произвольное количество plural forms и формулу их выбора. в русском языке их три и ngettext прекрасно это обрабатывает
0
tenshi #
у нас нет проблем с техпроцессом именно потому что геттекст не используется

char * ngettext (const char * msgid, const char * msgid_plural, unsigned long int n);

где ты тут узрел 3 формы? или предлагаешь писать в коде 2 формы, а третюю пусть приписывают переводчики?
0
muromec #
>или предлагаешь писать в коде 2 формы, а третюю пусть приписывают переводчики?

это можно сделать дофигищей разных способов без оверхеда с двойным переводом. тот же .po файл для русского языка можно генерировать по исходному коду.

тем не менее, писать в коде русские сообщения и завязываться на фиксированное количество plural forms — моветон, ящитаю.
0
tenshi #
каким таким образом можно генерировать по исходному коду русские тексты, если в исходниках можно указать лишь 2 формы?

то есть завязываться на английский и 2 формы — это нормально, а на русский и 3 формы — моветон? х)
0
muromec #
>каким таким образом можно генерировать по исходному коду русские тексты, если в исходниках можно указать лишь 2 формы?

напрягите фантазию

>то есть завязываться на английский и 2 формы — это нормально, а на русский и 3 формы — моветон?

у меня резист от троллинга на плюс пицот.

вариант «английский в коде, русский в .po файле» — определенно лучше «русский в коде, что делать дальще — непонятно»
0
tenshi #
резист от интеллекта у тебя тоже зашкаливает…
0
muromec #
ну то есть как локальное решение, чисто для русского языка — можно и свой велосипед, но это именно непортабельное локальное решение — по сути, грязный хак, мешающий нормальной локализации.
–4
Buran #
Супер, спасибо автору! Давно мечтал о таком решении (перенес в свои проекты).
–4
Buran #
А вообще, автор, НА ХУЙ С ХАБРА!
+2
kirilloid #
«Вон из профессии!» =)
–5
Buran #
Откуда у него такая карма, господи?! На чем он поднялся?
+3
v_k #
залоговые аукционы
+1
AusTiN #
за что вы его так?
+1
dector #
Наверное, англо-язычные роботы не палились.
0
a_koposov #
Вот в одну строчку
public static function make($n, $form_1, $form_2, $form_3){
return ($n%10==1&&$n%100!=11? $form_1 :($n%10>=2&&$n%10<=4&&($n%100<10||$n%100>=20)? $form_2: $form_3));
}
+1
ymik #
угу. А теперь попробуем просклонять:
1 год; 2,3,4 года; 5,6,7,8,9 лет

И таких слов-исключений попадается немало
0
v_k #
напишите пожалуйста какие еще есть
0
ymik #
Ну, к примеру, практически все существительные у которых есть множественная форма.
ножницы (одни, двое, трое ножниц, ножницы, 4, 5 шт, шестеро, семеро, восемь, девять ножниц),
торги
хлопья (хлопья кукурузные, 3 шт, но не трое хлопьев или три хлопья)
0
no_smoking #
И я добавлю :)

Ext.util.Format.wformat = function(number,forms){
        var n = parseInt(number,10);
        var nplurals=3;
        var plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);
        plural = (n==0)?0:plural + 1;
        if(forms.length > plural) return forms[plural].replace("$",n);
        return "";
    }

Собственно эта функция помещена в обьект Ext.util.Format для того чтобы ее можно было использовать в Шаблонах ExtJs вот так

var tpl = new Ext.XTemplate('{year:(["нет","Один год","$ года","$ лет"])}');

Ну и конечно не кто не заприщает ее использовать и без шаблонов :)
+1
Cellard #
И никто, никто не вспомнил про ngettext(), который старше большинства из нас. И прямо в википедии есть уже готовая реализация кода.
0
Mirror #
Кстати, а его всегда на хостингах включают?
Что касаемо вообще gettext, останавливает от его использования формат библиотек, усложнённо как-то всё, с компилированием.
0
muromec #
>останавливает от его использования формат библиотек, усложнённо как-то всё, с компилированием.

вот он — php-way! проще навелосипедить, чем разобраться в стандартном решении.

>с компилированием.

конечно с компилированием, не парсить же каталог при каждом вызове, CPU time на шаред-хостинке халявный, да?
0
Mirror #
Стандартное решение не всем и не всегда подходит, зачастую велосипед лучше.
0
muromec #
причины «объективно не подходит» и «проще навелосипедить» — немного разные.
0
Mirror #
Невозможность работы решения на хостинге, где PHP собран без поддержки gettext — для меня лично более чем объективная причина. Проще и, я думаю, правильнее, дать пользователю возможность работать с CMS (например) не заставляя его переписываться с хостером (а сперва он напишет разработчику, или просто плюнет на «не рабочее» решение).
0
cadmi #
0
Paskal #
У Яндекса есть публичный сервис — "Склонятор". Возможно, в данном случае это как стрельба из пушки по воробьям, но, надеюсь, кому-нибудь прочитавшему это пригодится.
Думается мне, можно написать под этот сервис универсальную обёртку на java.
0
Mirror #
Примерно такую? pipes.yahoo.com/pipes/pipe.info?_id=sBgMDDk93hGMjlvuPm7D0g
Правда, на самом деле какое-то глупое использование «Склонятора».
0
Paskal #
Во-первых, это у меня не работает, во-вторых, я имел в виду вывод текста, связанного с числительными, автоматом через склонятор. То есть, например, брать в некую обёртку число и существительное, а выдавать и то и то в нужной форме — не через формы ввода, а прямо на генерируемой странице.
0
Mirror #
Я не проверял, просто вспомнил. Скорей всего Яндекс забанил доступ к своим сервисам от IP Yahoo Pipes, потому и сломалось. А там вроде как раз так и было сделано.
0
AusTiN #
У меня так:

/**
 * Функция склонения числительных в русском языке
 *
 * @param int    $number Число которое нужно просклонять
 * @param array  $titles Массив слов для склонения
 * @return string
 **/
function DigitCase($number, $titles)
{
	//отсекаем минусовые значения
	$number = str_replace("-","",$number);
        $cases = array (2, 0, 1, 1, 1, 2);
        return $titles[ ($number%100>4 && $number%100<20)? 2 : $cases[min($number%10, 5)] ];
}
0
Ferroman #
Use the ngettext, Luke!
+1
annihilator #
Вот вам чуть ли не для всех языков сразу: github.com/alien/ali-public/blob/master/samples/Ali/I18N/PluralForms.pm
0
Rudakovsky #
На жабе для склонения дней:
public class Misc {
  public static String getDayText (int numberOfDays) {
    int x;
    x = numberOfDays % 100;
    if (> 20) {
       x = x % 10;
    }
    if (> 4) {
       return "дней";
    } else {
       switch (x) {
         case 1: return "день";
         case 0: return "дней";
         default: return "дня";
       }
    }
}

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.