Самый элегантный способ борьбы с одиночками / множественными числами?
Допустим, вы создаете программное обеспечение для блога и хотите показать количество комментариев, полученных в записи. Вы можете сделать это так:
[Entry title]
[Content........]
[ <?php print($numComments;) ?> Comments]
Что может привести к:
[Entry title]
[Content........]
5 Comments
Но если запись содержит только 1 комментарий, я хочу, чтобы в строке было написано "Комментарий", а не "Комментарии". И в линии if/else
уродливые и повторяющиеся.
Какой лучший способ справиться с этим?
7 ответов
Пожалуйста, используйте ngettext
функция для таких вещей, как это. Это позволяет вам раз и навсегда разобраться с множественным числом на английском и других языках. Вы используете это так:
printf(ngettext("%d Comment", "%d Comments", $numComments), $numComments);
ngettext
функция вернет первую строку формата ("%d
Comment"
) если есть только один комментарий и вторая строка формата ("%d Comments"
) если есть еще. printf
Функция поместит число в строку.
Это может показаться большой работой, но она очень мощная: она работает с языками, которые имеют более чем одну форму множественного числа (!) - они действительно существуют. В руководстве по PHP приведен пример слова "окно", которое на некоторых экзотических языках, которые я не узнаю, становится "1 окно", "2 окна" и " 5 окен ".
Если вы используете ngettext
, тогда ваши будущие пользователи из дальних стран будут вам очень благодарны:-)
Изменить: как предлагается в комментариях, есть одна функция, чтобы сделать выше:
function pluralize($num, $singleWord, $pluralWord) {
return printf(ngettext($singleWord, $pluralWord, $num), $num);
}
По умолчанию, xgettext
не будет распознавать эту новую функцию, но вы можете добавить ее с помощью --keyword
флаг. Данный файл test.php
с
echo ngettext("foo", "foos", 1);
echo pluralize(2, "bar", "bars");
Вы можете извлечь строки с
xgettext --keyword=pluralize:2,3 test.php
Результирующий messages.po
В файле есть такие записи:
#: test.php:7
msgid "foo"
msgid_plural "foos"
msgstr[0] ""
msgstr[1] ""
#: test.php:8
msgid "bar"
msgid_plural "bars"
msgstr[0] ""
msgstr[1] ""
Переводчик заполняет каждую форму множественного числа, и с правильно сформированной строкой "Plural-Forms" в заголовке каталога сообщений вы сможете поддерживать все языки.
Почему бы не потратить время, чтобы очеловечить вещи еще больше....
switch ($numComments)
{
case 0:
echo "Be the first to write a comment";
break;
case 1:
echo "Just one comment so far";
break;
default:
echo "There are $numComments comments";
}
Меня удивляет, что никто еще не предлагал этого, но я обычно использую условный оператор:
string commentWord = numComments != 1 ? "Comments" : "Comment";
Примечание: строка, конечно, вообще не должна быть жестко запрограммирована, а должна быть загружена из некоторого репозитория ресурсов, где она хранится с заполнителем форматирования для числа, чтобы вы могли работать с языками, где число должно появляться последним (или в середине):
// should load "{0} Comments" or "{0} Comment" if we run in an English locale
string comments = string.Format(
numComments != 1 ? GetResource("Comments") : GetResource("Comment"),
numComments);
Создайте функцию, которая принимает число и слово и возвращает строку, содержащую оба. Он добавит "s" (или сверится со словарем, который вы строите), когда число больше 1.
Не самый элегантный, но самый простой - вывести "Комментарий (и)".
[Entry title]
[Content........]
1 Comment(s)
В C/C++ вы можете сделать следующее. Вы можете сделать что-то подобное в PHP.
printf("%d %s\n", numComments, numComments == 1 ? "Comment" : "Comments");
Следующее также работает, но вы можете столкнуться с проблемами с \b
(backspace) неправильно обрабатывается в разных реализациях.
printf("%d Comment%s\n", numComments, numComments == 1 ? " \b" : "s");
С помощью \0
(нулевой символ), чтобы ничего не печатать, вместо этого напечатал пробел в моей реализации.
Посмотрите на модуль инфлектора Rails. Это обеспечивает хорошее, централизованное и настраиваемое решение этой проблемы.