Comments 53
А ещё можно учесть translate.sourceforge.net/wiki/l10n/pluralforms и сделать более модную ф-ию.
Только внимание, там код для C, а в C несколько иная интерпретация тренарного оператора (?:) — придётся к тем формулам скобочки добавить, чтобы правильно работало.
Только внимание, там код для C, а в C несколько иная интерпретация тренарного оператора (?:) — придётся к тем формулам скобочки добавить, чтобы правильно работало.
$int = round(1,999);
$text = declension($int, array('попугай','попугая','попугаев');
function declension($digit, $expr, $onlyword = false) {
if(empty($expr[2])) $expr[2]=$expr[1];
$i = preg_replace('/[^0-9]+/s','',$digit)%100; //intval не всегда корректно работает
if ( $onlyword ) $digit='';
if ( $i >= 5 AND $i <= 20 ) $res=$digit.' '.$expr[2];
else {
$i%=10;
if ($i==1) $res=$digit.' '.$expr[0];
elseif ( $i >= 2 && $i <= 4 ) $res = $digit .' '. $expr[1];
else $res = $digit.' '.$expr[2];
}
return trim($res);
}
(с) где-то с интенета
$text = declension($int, array('попугай','попугая','попугаев');
function declension($digit, $expr, $onlyword = false) {
if(empty($expr[2])) $expr[2]=$expr[1];
$i = preg_replace('/[^0-9]+/s','',$digit)%100; //intval не всегда корректно работает
if ( $onlyword ) $digit='';
if ( $i >= 5 AND $i <= 20 ) $res=$digit.' '.$expr[2];
else {
$i%=10;
if ($i==1) $res=$digit.' '.$expr[0];
elseif ( $i >= 2 && $i <= 4 ) $res = $digit .' '. $expr[1];
else $res = $digit.' '.$expr[2];
}
return trim($res);
}
(с) где-то с интенета
Это dklab'овский код. Далеко не самый оптимальный, надо вам сказать.
не оптимальный по скорости?
Да. И вообще, как по мне он не очень удобен и для программиста.
Вас беспокоит скорость этого куска кода? Вы привыкли экономить на спичках?
а что будет удобней для «программиста»? $bananes = format_num(100);? это самый оптимальный вариант, но боюсь функция не угадает в чем мерять переданные единицы — в бананах или попугаях…
Можно сделать еще проще и мучать смарти
function okonchanie($c, $m, $o, $t)
{
$c = abs($c);
if ($c % 100 == '1' or ($c % 100 > '20') and ($c % 10 == '1')) return $o;
else if ($c % 100 == '2' or ($c % 100 > '20') and ($c % 10 == '2')) return $t;
else if ($c % 100 == '3' or ($c % 100 > '20') and ($c % 10 == '3')) return $t;
else if ($c % 100 == '4' or ($c % 100 > '20') and ($c % 10 == '4')) return $t;
else return $m;
}
пример
okonchanie($count, 'комментариев', 'комментарий', 'комментария')
function okonchanie($c, $m, $o, $t)
{
$c = abs($c);
if ($c % 100 == '1' or ($c % 100 > '20') and ($c % 10 == '1')) return $o;
else if ($c % 100 == '2' or ($c % 100 > '20') and ($c % 10 == '2')) return $t;
else if ($c % 100 == '3' or ($c % 100 > '20') and ($c % 10 == '3')) return $t;
else if ($c % 100 == '4' or ($c % 100 > '20') and ($c % 10 == '4')) return $t;
else return $m;
}
пример
okonchanie($count, 'комментариев', 'комментарий', 'комментария')
Отделяем представление от логики, отделяем. Если же очень необходимо:
Copy Source | Copy HTML
- /**
- * @desc Функция получения русскоязычного варианта окончания для переданного числа
- * @param float $value Число, для которого надо подобрать окончание
- * @param array $names Массив имён вида [0]имя одного; [1]имя от 2 до 4; [2]имя 0 и от 5 до 20
- * @return string Возвращает соответствующее числу слово
- */
- function getNumberWord($value,$names){
- $temp = strval($value);
- $temp = $temp[utf8_strlen($temp)-1];
- return (($temp>1 and $temp <5 and (intval($value)>19 or intval($value)<10))?$names[1]:($temp==1?$names[0]:$names[2]));
- }
И еще незабываем что код пишем для людей.
Nice guide — people should be able to recognise ternary operators even if they choose not to use them.
А кроме всего прочего по скромному ИМХО вашего покорного слуги, код с тернарными сравнениями выглядит намного чище и понятнее, чем простыни условий. Но это, как я уже сказал, ИМХО.
Я лично предпочитаю рубить строки с длинными условиями с тернарным оператором.
Опять же имхо, запись этой строки как
Copy Source | Copy HTML
для меня выглядит так:- — операции определения значения переменной ИМХО должны быть записаны с использованием тернарных условий (потому как в строку писать if — это некомпетентность). Если применяются операторы или производится несколько действий — тогда да, тернарки неприменимы
- — некрасиво ибо простыня
- — если «экономить на спичках», то такая запись менее производительна (~0.5 секунды при выполнении 5000000 операций)
Я, видимо, был неправильно понят. Под фразой «рубить строку», я подразумевал использовать переносы, размещая тернарный оператор (и знак вопроса и двоеточие с новой строки). Как-то так:
Это просто для примера, обычно условия внутри тернарного оператора бывают длиннее, смотрится лучше.return (($temp>1 and $temp <5 and (intval($value)>19 or intval($value)<10)) ? $names[1] : ($temp==1 ? $names[0] : $names[2]));
я смотрю немногие тут сталкивались с числами больше ста
11 комментарий
*и не мучать смарти)
Мда, боян, и все тут же принялись постить свои функции… Странно, пятница, наверное.
пару дней назад была тема о подобных же рассуждениях. там же приводились правильные ссылки по расчёту множественных форм.
повторю ссылку ещё раз: translate.sourceforge.net/wiki/l10n/pluralforms
повторю ссылку ещё раз: translate.sourceforge.net/wiki/l10n/pluralforms
Не могу не заметить, что у вас ошибка в логике функции.
Например, для 112 результат будет неверным.
Например, для 112 результат будет неверным.
делал такое для XSLT (вдруг кому нужно):
Copy Source | Copy HTML
- <?xml version="1.0" encoding="utf-8"?>
- <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- <xsl:output method="xml" indent="no" encoding="utf-8" />
-
- <xsl:template name="words">
- <xsl:param name="count">0</xsl:param>
- <xsl:param name="wForm1" /> <!-- 1 предмет -->
- <xsl:param name="wForm2" /> <!-- 2 предмета-->
- <xsl:param name="wForm3" /> <!-- 10 предметов -->
-
- <xsl:variable name="final" select="number(substring($count, string-length($count)-1))" />
- <xsl:choose>
- <xsl:when test="$final mod 10 = 0">
- <xsl:value-of select="$wForm3" />
- </xsl:when>
- <xsl:when test="20 > $final and $final > 10">
- <xsl:value-of select="$wForm3" />
- </xsl:when>
- <xsl:when test="$final = 1 or $final mod 10 = 1">
- <xsl:value-of select="$wForm1" />
- </xsl:when>
- <xsl:when test="$final mod 10 > 4">
- <xsl:value-of select="$wForm3" />
- </xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="$wForm2" />
- </xsl:otherwise>
- </xsl:choose>
- </xsl:template>
-
- </xsl:stylesheet>
Для symfony было бы приятно иметь такое
А gettext уже не в моде?
Мое решение: модификатор.
Пример: {$total_pages|incline_word:«всего %d страница»:«всего %d страницы»:«всего %d страниц»:«нет ни одной страницы»}
Решение как в gettext конечно правильнее в плане интернационализации, но если мы целимся только на русскоязычную аудиторию, то можно и так, тем более что так удобно все прописывать в одном шаблоне)
Пример: {$total_pages|incline_word:«всего %d страница»:«всего %d страницы»:«всего %d страниц»:«нет ни одной страницы»}
Решение как в gettext конечно правильнее в плане интернационализации, но если мы целимся только на русскоязычную аудиторию, то можно и так, тем более что так удобно все прописывать в одном шаблоне)
Мне удобнее всего использовать следующую функцию:
function plural_word($first_word, $second_word, $third_word, $number)
{
if ($number > 100) $number = $number % 100;
if ($number > 20) $number = $number % 10;
switch ($number)
{
case 1:
return $first_word; // «год»
case 2:
case 3:
case 4:
return $second_word; // «года»
default:
return $third_word; // «лет»
}
}
function plural_word($first_word, $second_word, $third_word, $number)
{
if ($number > 100) $number = $number % 100;
if ($number > 20) $number = $number % 10;
switch ($number)
{
case 1:
return $first_word; // «год»
case 2:
case 3:
case 4:
return $second_word; // «года»
default:
return $third_word; // «лет»
}
}
Русский без роботизма — это когда у тебя 458 комментариев, а ты пишешь «несколько сотен комментариев», когда ты автоматически округляешь размерфайла в списке файлов в несколько тысяч байт до килобайтов, а в несколько тысяч килобайтов до десятых мегабайта, когда ты пишешь «последний коммент был добавлен несколько минут назад», а не «12 января, 12:35» и так далее.
А писать про банальное правило 1-2-5 — это, простите, кармадроч.
А писать про банальное правило 1-2-5 — это, простите, кармадроч.
Лишать пользователя потенциально полезной информации (иногда интересна точность) — тоже не стоит.
Согласен, необходимо предоставлять альтернативный вариант доступа к точной информации (если кому-то она будет интересна). Однако необходимо разумно подходить к выбору, что будет интересно подавляющему большинству в первую очередь — «точные данные» или «ориентирование в ситуации».
Переменная temp с 7-ю употреблениям, это круто, конечно.
предложу свой вариант (конечно не идеал, но коротко и ясно — берется последняя цифра и уже исходя из нее подставляется слово в нужном падеже):
function get_number($n,$w1,$w2,$w3){
$n = (string)$n;
$s = (int)$n[strlen($n)-1];
if ($s==1 && $s!=0){
return $w1;
} elseif ($s
function get_number($n,$w1,$w2,$w3){
$n = (string)$n;
$s = (int)$n[strlen($n)-1];
if ($s==1 && $s!=0){
return $w1;
} elseif ($s
блин как увижу какой-нибудь такой гид — аж волосы дыбом встают — а ведь когда-то лет эдак 7-8 назад я был такой же юнец… )))
сейчас же — перевод на 30-40 языков, прекрасное понимае что числительных форм может быть 2, 3 (рус.) и даже 4 штуки. А ещё числительную форму для каждого языка можно выразить формулой.
Пример для русского языка:
n%10==1 && n%100!=11? 0: n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20)? 1: 2
А ещё — существует gettext, который кушает эту формулу и сам всё сотворит. Ну на худой конец можно лингвиста от кьют, если на кьюте писать что-нибудь.
К слову, под пхп gettext встроенный. И ОЧЕНЬ (!!!!!!!!!!!!!!!!!) простой. Делайте сразу правильно, не повторяйте чужих ошибок)
сейчас же — перевод на 30-40 языков, прекрасное понимае что числительных форм может быть 2, 3 (рус.) и даже 4 штуки. А ещё числительную форму для каждого языка можно выразить формулой.
Пример для русского языка:
n%10==1 && n%100!=11? 0: n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20)? 1: 2
А ещё — существует gettext, который кушает эту формулу и сам всё сотворит. Ну на худой конец можно лингвиста от кьют, если на кьюте писать что-нибудь.
К слову, под пхп gettext встроенный. И ОЧЕНЬ (!!!!!!!!!!!!!!!!!) простой. Делайте сразу правильно, не повторяйте чужих ошибок)
мои 5 копеек:
и не стоит в этом случае говорить, что «код пишем для человека», ибо эта функция реализует 100% требований, а так же является минимальной неделимой частью — внутри раскапывать нечего
/**
* Возвращает склонение числа
*
* @param integer $number
* @param array $titles - массив значений. Например, array("гривна", "гривны", "гривен")
* @return string
*/
function declOfNum( $number, $titles ) {
$cases = array( 2, 0, 1, 1, 1, 2 );
return $titles[ ( $number % 100 > 4 && $number % 100 < 20 ) ? 2 : $cases[ min( $number % 10, 5 ) ] ];
}
и не стоит в этом случае говорить, что «код пишем для человека», ибо эта функция реализует 100% требований, а так же является минимальной неделимой частью — внутри раскапывать нечего
>{number2word number=$your_number name1=Строка name2=Строки name3=Строк}
Очень громоздко :)
Плагин, hg.balancer.ru/hgwebdir/bors-core/file/1a7622f87584/engines/smarty/plugins/modifier.sklon.php:
Вызывается так:
Всего в БД {$n} {$n|sklon:'сообщение':'сообщения':'сообщений'}.
Или:
Всего в БД {$n} сообщени{$n|sklon:'е':'я':'й'}.
Или:
Всего в БД {$n} сообщени{$n|sklon:'е, я, й'}.
Плюс просто функция sklon: hg.balancer.ru/hgwebdir/bors-core/file/15136adfd8fb/inc/strings.php
(да, лучше бы обозвать declension, но sklon — запомнить проще ;) )
Очень громоздко :)
Плагин, hg.balancer.ru/hgwebdir/bors-core/file/1a7622f87584/engines/smarty/plugins/modifier.sklon.php:
<?php 2 function smarty_modifier_sklon($n, $s1, $s2=NULL, $s5=NULL) // 1 нож 2 ножа 5 ножей 3 { 4 if($s2 === NULL) 5 list($s1, $s2, $s5) = explode(',', $s1); 6 7 $ns=intval(substr($n,-1)); 8 $n2=intval(substr($n,-2)); 9 10 if($n2>=10 && $n2<=19) return $s5; 11 if($ns==1) return $s1; 12 if($ns>=2&&$ns<=4) return $s2; 13 return $s5; 14 }
Вызывается так:
Всего в БД {$n} {$n|sklon:'сообщение':'сообщения':'сообщений'}.
Или:
Всего в БД {$n} сообщени{$n|sklon:'е':'я':'й'}.
Или:
Всего в БД {$n} сообщени{$n|sklon:'е, я, й'}.
Плюс просто функция sklon: hg.balancer.ru/hgwebdir/bors-core/file/15136adfd8fb/inc/strings.php
(да, лучше бы обозвать declension, но sklon — запомнить проще ;) )
Числительными все впорядке, задача стара как мир, и решений море.
А вот как обстоит дело с падежами?
Есть ли реальный способ склонять слово по падежам? Ну или хотя бы по роду.
Например, создана страница, создан материал.
Может быть там можно с окончаниями помудрить?
А вот как обстоит дело с падежами?
Есть ли реальный способ склонять слово по падежам? Ну или хотя бы по роду.
Например, создана страница, создан материал.
Может быть там можно с окончаниями помудрить?
я тут тоже наваял для XSLT и PHP. Ничто не мешает для JS переделать и тп.
mynsa04.livejournal.com/200981.html
mynsa04.livejournal.com/200981.html
Sign up to leave a comment.
Русский без роботизма, часть 1