Сниппет Declension для склонения слов
Предлагаю вашему вниманию небольшой сниппет для вывода склонения. Работает как фильтр вывода, использовать нужно примерно так:
Как видите, он применяется к любым тегам с числами, а в options нужно указать три формы слова (1 яблоко, 2 яблока и 10 яблок) для русского, или 2 (1 ticket, 2 tickets) для английского.
Для экспериментов можно повызывать сниппет с произвольными числами:
Как вы уже догадались, сниппет был написан для оформления баланса на хостинге, но я подумал, что вам он тоже пригодится.
Если кто-то оформит его в готовый пакет и выложит в репозиторий — не возражаю. Уже давно есть.
[[+balls]] [[+balls:declension=`балл,балла,баллов`]]
Как видите, он применяется к любым тегам с числами, а в options нужно указать три формы слова (1 яблоко, 2 яблока и 10 яблок) для русского, или 2 (1 ticket, 2 tickets) для английского.
Для экспериментов можно повызывать сниппет с произвольными числами:
15 [[*id:input=`15`:declension=`яблоко,яблока,яблок`]]
Полный код под катом.<?php
/**
* Declension of words
* This algorithm taken from https://github.com/livestreet/livestreet/blob/eca10c0186c8174b774a2125d8af3760e1c34825/engine/modules/viewer/plugs/modifier.declension.php
*/
/** @var array $scriptProperties */
$number = $modx->getOption('input', $scriptProperties, 0);
$forms = $modx->getOption('options', $scriptProperties, '');
if (is_string($forms)) {
if ($forms[0] == '[') {
$forms = $modx->fromJSON($forms);
}
else {
$forms = explode(',', $forms);
}
}
if (!is_numeric($number)) {
return;
}
if (!is_array($forms) || empty($forms)) {
return 'Вы должны указать формы слов для склонения. Например [[*id:declension=`яблоко,яблока,яблок`]].';
}
if (count($forms) == 3) {
$mod100 = $number % 100;
switch ($number % 10) {
case 1:
if ($mod100 == 11) {
$text = $forms[2];
}
else {
$text = $forms[0];
}
break;
case 2:
case 3:
case 4:
if (($mod100 > 10) && ($mod100 < 20)) {
$text = $forms[2];
}
else {
$text = $forms[1];
}
break;
case 5:
case 6:
case 7:
case 8:
case 9:
case 0:
default:
$text = $forms[2];
}
}
elseif (count($forms) == 2) {
if ($number == 1) {
$text = $forms[0];
}
else {
$text = $forms[1];
}
}
else {
$text = 'Недостаточно форм слов для склонения';
}
return $text;
Как вы уже догадались, сниппет был написан для оформления баланса на хостинге, но я подумал, что вам он тоже пригодится.
Комментарии: 30
Спасибо, забираю.)
а он через аякс не обрабатывает {товара, товаров, товар} в чанке корзины? )
Столкнулся однажды с такой же проблемой, так что в оф. репозитории похожее дополнение есть)
Я искал как declension =)
Поправил, теперь по этому слову его можно найти)))
Грамотно! От души, друг! Ещё бы осуществить его работу под минишоп2. :)
а в чем проблема? он же как фильтр работает
Василий, а на сколько оправдано так много строк кода? У нас в смарти-плагине всего несколько строчек. Все тоже самое, ниразу не было замечено, чтобы склонения как-то не так были.
{$count|spell:«яблоко»:«яблока»:«яблок»}
{$count|spell:«яблоко»:«яблока»:«яблок»}
Это адаптация чужого кода, плюс здесь можно указывать 2 формы для английских слов.
Ну и лично я люблю, когда понятно, а не коротко. PHP всё равно оптимизирует исходники при выполнении.
Ну и лично я люблю, когда понятно, а не коротко. PHP всё равно оптимизирует исходники при выполнении.
Ясно. ОК.
На JS тоже так можно =)
function declineWord(number, titles) {
cases = [2, 0, 1, 1, 1, 2];
return titles[ (number%100>4 && number%100<20)? 2 : cases[(number%10<5)?number%10:5] ];
}
declineWord(2,['кнопка','кнопки','кнопок']);//2 кнопки
Не уверен, что нужна его реализация в виде отдельного пакета, хотя я бы наверное собрал несколько таких мелочей в один пакет под названием Утилиты. Сюда можно добавить еще форматирование чисел, дат и тд, тогда будет иметь смысл делать пакет. В остальном, из того, что видел, реализация declension занимает 20 строчек кода максимум.
В остальном, из того, что видел, реализация declension занимает 20 строчек кода максимум.Обрати внимание — это код из LiveStreet, ты его, вероятно, не видел.
Можно и в одну строку сжать — не проблема. От этого кому лучше будет?
Я вообще не понимаю экономии на строках. А есть еще люди, которые на скобках экономят, и на пробелах. Должно быть понятно, тем более, когда выкладываешь что-то для других.
А еще на здоровье экономят :-P
Я не к формату придираюсь, и не считаю пробельные строки и отступы, это же понятно.
Я к тому, что функция нужная, но не такая важная, чтобы из нее пакет делать. Алгоритм элементарный, такие задачки на информатике в школе проходить нужно и пишется он за пару минут. Это сейчас не укор твоим способностям, они неоспоримы.
Я к тому, что функция нужная, но не такая важная, чтобы из нее пакет делать. Алгоритм элементарный, такие задачки на информатике в школе проходить нужно и пишется он за пару минут. Это сейчас не укор твоим способностям, они неоспоримы.
Да про пакет понятно, тем более, что он уже есть.
Давно пользовался modx.com/extras/package/numeralstring
Но поскольку для новых версий MODX его из репозитория не скачать, можно использовать обычный сниппет:
Но поскольку для новых версий MODX его из репозитория не скачать, можно использовать обычный сниппет:
<?php
/**
*
* @package numeralString
* @author Kirill A. <kirusanov@gmail.com>
*
* Числительное по-русски.
* На выходе число и три варианта написания: 1 - "*уй", 2 - "*уя", 5 - "*уев".
* Никаких чанков для вывода - просто возвращает один из вариантов числительного.
*
* @property count число
* @property one числительное для 1
* @property two числительное для 2
* @property five числительное для 5
*
*/
$count = !empty($scriptProperties['count']) ? abs((int)$scriptProperties['count']) : 0;
$one = !empty($scriptProperties['one']) ? $scriptProperties['one'] : 'голос';
$two = !empty($scriptProperties['two']) ? $scriptProperties['two'] : 'голоса';
$five = !empty($scriptProperties['five']) ? $scriptProperties['five'] : 'голосов';
$showNumber = !empty($scriptProperties['showNumber']) ? $scriptProperties['showNumber'] : 0;
if ($showNumber == 1) {
if (preg_match('/^1$|\d*[02-9]1$/', $count)) { return $count.' '.$one; }
if (preg_match('/^[2-4]$|\d*[02-9][2-4]$/', $count)) { return $count.' '.$two; }
return $count.' '.$five;
}
else {
if (preg_match('/^1$|\d*[02-9]1$/', $count)) { return $one; }
if (preg_match('/^[2-4]$|\d*[02-9][2-4]$/', $count)) { return $two; }
return $five;
}
А дальше вызывать:[[!numeralString? &count=`123` &one=`голос`&two=`голоса`&five=`голосов`]]
И всё же, может быть оформить это решение как отдельный пакет для репозитория modstore.pro/ ?
Почему я поднял этот вопрос:
потому что сейчас я решил «как обычно» не заморачиваясь скачать пакет «Units» из офф-репозитория, но его там не оказалось (не знаю, конфликт версий с MODX наверное). Дальше я вспомнил про этот сниппет и решил вбить его здесь на сообществе в поиск. Но подобрать подходящее слово у меня не получилось, и по «Units» тоже эта статья не нашлась. И пришлось мне кликать по пагинации раз 20 пока я не нашел эту статью (в закладках в профиле тоже куча статей и также долго пришлось её искать).
Я понимаю, что решение для кого-то несложное, но зачем каждый раз его велосипедить, если быстрее скачать и использовать?
Почему я поднял этот вопрос:
потому что сейчас я решил «как обычно» не заморачиваясь скачать пакет «Units» из офф-репозитория, но его там не оказалось (не знаю, конфликт версий с MODX наверное). Дальше я вспомнил про этот сниппет и решил вбить его здесь на сообществе в поиск. Но подобрать подходящее слово у меня не получилось, и по «Units» тоже эта статья не нашлась. И пришлось мне кликать по пагинации раз 20 пока я не нашел эту статью (в закладках в профиле тоже куча статей и также долго пришлось её искать).
Я понимаю, что решение для кого-то несложное, но зачем каждый раз его велосипедить, если быстрее скачать и использовать?
Спасибо!
На одном форуме народ соревновался в самом «компактном» решении этой задачи ))
Победило такое решение:
Победило такое решение:
<?php
if($n!=''){
return= $n%10==1&&$n%100!=11?$w1:($n%10>=2&&$n%10<=4&&($n%100<10||$n%100>=20)?$w2:$w3);
}
Ну и вызываем:[[Snippet? &n=`123` &w1=`год` &w2=`года` &w3=`лет` ]]
Интересное решение! В закладки!
Да, только "=" после return долой, естессно ))):
<?php
if($n!=''){
return $n%10==1&&$n%100!=11?$w1:($n%10>=2&&$n%10<=4&&($n%100<10||$n%100>=20)?$w2:$w3);
}
А как для корзины minishop2 прикрутить данное решение? Я так понимаю из-за аякса этот код не работает.
Подскажите пожалуйста, в документации про Парсер pdoTools есть такой модификатор:
declension (decl) — склоняет слово, следующее за числом по правилам русского языка. Например: 1 яблоко, 2 яблока, 10 яблок.
и пример:
чанк обёртки комментариев содержит переменную {$total} — количество комментариев. Хочу добавить склонение, но оно не хочет работать, хм… вот чанк tpl_commentsWrapper
declension (decl) — склоняет слово, следующее за числом по правилам русского языка. Например: 1 яблоко, 2 яблока, 10 яблок.
и пример:
{6 | declension : 'яблоко|яблока|яблок'} // яблок
{3 | declension : 'яблоко|яблока|яблок' : true} // 3 яблока
{101 | decl : 'яблоко,яблока,яблок' : false : ','} // яблоко
У меня для статей сделаны комментарии при помощи компонента Tickets, теперь к сути:чанк обёртки комментариев содержит переменную {$total} — количество комментариев. Хочу добавить склонение, но оно не хочет работать, хм… вот чанк tpl_commentsWrapper
<section class="inner-bottom-xs comments">
<h3 class="title">(<span id="comment-total">{$total}</span>) {$total | declension:'Комментарий|Комментария|Комментариев'}- </h3>
<div id="comments-wrapper">
<div class="comment-list" id="comments">
{$comments}
</div>
</div>
<div id="comments-tpanel">
<div id="tpanel-refresh"></div>
<div id="tpanel-new"></div>
</div>
</section>
Может подскажете что не так делаю, ибо не склоняет переменную. Если подставлять цифры то склоняет, а переменную не хочет.
В новых версиях pdoTools общее количество результатов не считается, если не указать это специально, для скорости.
Так что сейчас у тебя нет этой переменной, он пустая, и количество комментариев выставляется через javascript.
Укажи &setTotal=`1` в вызове TicketsComments, и всё должно заработать.
Так что сейчас у тебя нет этой переменной, он пустая, и количество комментариев выставляется через javascript.
Укажи &setTotal=`1` в вызове TicketsComments, и всё должно заработать.
Спасибо. Заработало. А сильно это на скорость влияет?
Скорость надо проверять, зависит от многих факторов, но на большом количестве записей подсчёт результатов может украсть полсекунды или больше.
Да и дело даже не в скорости, а в том, что при добавлении нового комментария их общее количество изменится, а склонение — нет.
Поэтому лучше или оставить как есть, без склонения, или делать его на js — и тогда total вообще не нужен.
Да и дело даже не в скорости, а в том, что при добавлении нового комментария их общее количество изменится, а склонение — нет.
Поэтому лучше или оставить как есть, без склонения, или делать его на js — и тогда total вообще не нужен.
Меня вот что сбивает с толку, переменная total без модификатора выводит количество, а с модификатором не хочет… получается что количество без модификатора считается без настройки 'set total'=>1, а с модификатором надо включать эту настройку… буду знать, спасибо.
Еще раз — нет, не выводит она ничего.
Это после загрузки страницы javascript подставляет количество комментов. Открой исходник страницы и увидишь на месте переменной, что там пусто.
Это после загрузки страницы javascript подставляет количество комментов. Открой исходник страницы и увидишь на месте переменной, что там пусто.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.