Множественность сообщений пользователей
много раз, при создании сообщения, чтобы показать пользователю, сообщение будет содержать ряд что-то о котором я хочу сообщить клиенту.
я приведу пример: клиент выбрал ряд элементов от 1 и выше и нажал кнопку Удалить. Теперь я хочу дать сообщение подтверждения клиенту, и я хочу упомянуть количество элементов, которые он выбрал, чтобы свести к минимуму вероятность его ошибки, выбрав кучу элементов и нажмите "Удалить", когда он хочет удалить только один из них.
один из способов сделать общее сообщение следующим образом:
int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " item(s). Are you sure you want to delete it/them?";
"проблема" здесь в том случае, когда noofitemselected
это 1, и мы должны написать элемент и это вместо предметы и их.
моим нормальным решением будет что-то вроде этого
int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " " + (noofitemsselected==1?"item" : "items") + ". Are you sure you want to delete " + (noofitemsselected==1?"it" : "them") + "?";
это становится довольно длинным и довольно неприятным очень быстро, если есть много ссылок к множеству чисел внутри кода, и фактическое сообщение становится трудно читать.
Так что мои вопросы просто. Есть ли лучшие способы генерировать подобные сообщения?
редактировать
Я вижу, что многие люди очень повесили трубку в случае, если я упомянул, что сообщение должно отображаться внутри окна сообщения, и просто дал ответ о том, как избежать использования окна сообщения вообще, и это все хорошо.
но помните, что проблема плюрализации также относится к текстам других мест в программе в дополнение к окнам сообщений. Например, метка рядом с сеткой, отображающая количество строк, выбранных в сетке, будет иметь ту же проблему в отношении плюрализации.
таким образом, это в основном относится к большинству текста, который выводится каким-то образом из программ, а затем решение не так просто, как просто изменить программу, чтобы больше не выводить текст :)
25 ответов
Если есть какой-либо шанс, независимо от того, насколько мал, что это приложение нужно будет перевести на другие языки, то оба ошибаются. Правильный способ сделать это:
string message = ( noofitemsselected==1 ?
"You have selected " + noofitemsselected + " item. Are you sure you want to delete it?":
"You have selected " + noofitemsselected + " items. Are you sure you want to delete them?"
);
Это потому что разные языки обработки множества по-разному. Некоторые, такие как малайский, даже не имеют синтаксических множественных чисел, поэтому строки обычно идентичны. Разделение двух строк упрощает поддержку других языков в дальнейшем.
в противном случае, если это приложение предназначено для потребляемые широкой общественности и должен быть удобным для пользователя, то второй способ предпочтительнее. Извините, но я не знаю более короткого способа сделать это.
Если это приложение предназначено для использования только внутри вашей компании, то сделайте ярлык "item(s)"
вещь. Вам действительно не нужно никого впечатлять при написании кода enterprisy. Но я бы не советовал делать это для публично потребляемого приложения, потому что это создает впечатление, что программист ленив и, следовательно, ниже их мнение о качестве приложения. Поверь мне, такие мелочи имеют значение.
вы можете избежать всего этого беспорядочного множества, просто удалив элементы без какого-либо сообщения и предоставив пользователю действительно хорошее средство отмены. пользователи никогда ничего не читают. Вы должен построить хороший объект отмены в рамках программы в любом случае.
вы фактически получаете 2 преимущества, когда вы createe всеобъемлющего механизма отмены. Первое преимущество облегчает жизнь пользователя, позволяя ему / ей исправлять ошибки и минимизировать чтение. Второе преимущество это то, что ваше приложение отражает реальную жизнь, позволяя отменить нетривиальный рабочий процесс (а не только ошибки).
Я однажды написал приложение без использования одного диалога или сообщения подтверждения. Это потребовало серьезных размышлений и было значительно сложнее реализовать, чем использовать сообщения типа подтверждения. Но конечный результат был довольно приятным для использования в соответствии с его конечными пользователями.
Как насчет просто:
string message = "Are you sure you want to delete " + noofitemsselected + " item(s)?"
таким образом, вы устраняете трудности с соглашением о количестве и получаете еще более короткое, более точное сообщение об ошибке для пользователя в качестве бонуса. Мы все знаем пользователи не читают сообщения об ошибках в любом случае. Чем они короче, тем больше вероятность, что они по крайней мере взгляд в тексте.
или, вооружившись этим знанием, что пользователи не читают сообщения об ошибках, вы можете подойти к этому по-другому. Пропускать сообщение подтверждения в целом, и просто предоставить функцию отмены, которая просто работает, независимо от того, что было удалено. Большинство пользователей уже привыкли отменять операцию, когда они замечают, что это не то, что они хотели, и, вероятно, найдут это поведение более естественным, чем иметь дело с другим раздражающим всплывающим окном.
Как насчет того, что Java было в течение многих лет: java.текст.MessageFormat и ChoiceFormat? См.http://download.oracle.com/javase/1.4.2/docs/api/java/text/MessageFormat.html для получения дополнительной информации.
MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
form.applyPattern(
"There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}.");
Object[] testArgs = {new Long(12373), "MyDisk"};
System.out.println(form.format(testArgs));
// output, with different testArgs
output: The disk "MyDisk" are no files.
output: The disk "MyDisk" is one file.
output: The disk "MyDisk" are 1,273 files.
в вашем случае вы хотите что-то несколько проще:
MessageFormat form = new MessageFormat("Are you sure you want to delete {0,choice,1#one item,1<{0,number.integer} files}?");
преимущество этого подхода заключается в том, что он хорошо работает с пакетами i18n, и вы можете предоставить переводы правильно для языков (например, японский), которые не имеют понятия множественного числа или особые слова.
Я бы пошел с не жестким кодированием сообщения, но предоставлением двух сообщений в отдельном файле ресурсов. Как
string DELETE_SINGLE = "You have selected {0} item. Are you sure you want to delete it?";
string DELETE_MULTI = "You have selected {0} items. Are you sure you want to delete them?";
и затем подачи их в строку.Формат как
if(noofitemsselected == 1)
messageTemplate = MessageResources.DELETE_SINGLE;
else
messageTemplate = MessageResources.DELETE_MULTI;
string message = String.Format(messageTemplate, noofitemsselected)
Я думаю, что этот подход легче локализовать и сохранять. Все сообщения UI будут находиться в одном месте.
вы можете полностью обойти проблему, сформулировав сообщение по-другому.
string message = "The number of selected items is " + noofitemsselected + ". Are you sure you want to delete everything in this selection?";
первое, что я бы предложил: используйте string.Format
. Это позволяет вам сделать что-то вроде этого:
int numOfItems = GetNumOfItems();
string msgTemplate;
msgTemplate = numOfItems == 1 ? "You selected only {0} item." : "Wow, you selected {0} items!";
string msg = string.Format(msgTemplate, numOfItems);
далее, в приложениях WPF я видел системы, в которых строка ресурса была бы разделена разделителями каналов, чтобы иметь два сообщения: единственное и множественное сообщение (или нулевое/одно/много сообщений). Затем для анализа этого ресурса и использования соответствующей (отформатированной) строки можно использовать пользовательский конвертер, поэтому ваш Xaml выглядит примерно так:
<TextBlock Text="{Binding numOfItems, Converter={StaticResource c:NumericMessageFormatter}, ConverterParameter={StaticResource s:SuitableMessageTemplate}}" />
для английского языка, много ответов выше. Для других языков это сложнее, так как множественное число зависит от рода существительного и окончания слова. Некоторые примеры на французском языке:
обычные мужские:
Vous avez choisi 1 compte. Voulez-vous vraiment le supprimer.
Vous avez choisi 2 comptes. Voulez-vous vraiment les supprimer.
обычный женский
Vous avez choisi 1 table. Voulez-vous vraiment la supprimer.
Vous avez choisi 2 tables. Voulez-vous vraiment les supprimer.
нерегулярный мужской (заканчивается на "s")
Vous avez choisi 1 pays. Voulez-vous vraiment le supprimer.
Vous avez choisi 2 pays. Voulez-vous vraiment les supprimer?
та же проблема существует в большинстве латинских языков и ухудшается в немецком или русском, где есть 3 пола (макулин, женский и средний.)
вам нужно будет позаботиться, если ваша цель-обрабатывать больше, чем просто английский.
чтобы иметь возможность иметь несколько сообщений, которые можно будет правильно локализовать, я считаю, что было бы разумно сначала создать слой косвенности между числом и сообщением.
например, используйте некоторую константу, чтобы указать, какое сообщение вы хотите отобразить. Извлеките сообщение, используя некоторую функцию, которая скроет детали реализации.
get_message(DELETE_WARNING, quantity)
затем создайте словарь, который содержит возможные сообщения и варианты, и сделайте вариации знают, когда их следует использовать.
DELETE_WARNING = {
1: 'Are you sure you want to delete %s item',
>1: 'Are you sure you want to delete %s items'
>5: 'My language has special plural above five, do you wish to delete it?'
}
теперь вы можете просто найти ключ, который соответствует quantity
и интерполировать значение quantity
С этим сообщением.
Это упрощенный и наивный пример, но я действительно не вижу другого разумного способа сделать это и быть в состоянии обеспечить хорошую поддержку L10N и I18N.
вам придется перевести функцию ниже с VBA на C#, но ваше использование изменится на:
int noofitemsselected = SomeFunction();
string message = Pluralize("You have selected # item[s]. Are you sure you want to delete [it/them]?", noofitemsselected);
у меня есть функция VBA, которую я использую в MS Access, чтобы делать именно то, о чем вы говорите. Я знаю, что меня разорвут на куски за публикацию VBA, но все равно. Алгоритм должен быть очевиден из комментариев:
'---------------------------------------------------------------------------------------'
' Procedure : Pluralize'
' Purpose : Formats an English phrase to make verbs agree in number.'
' Usage : Msg = "There [is/are] # record[s]. [It/They] consist[s/] of # part[y/ies] each."'
' Pluralize(Msg, 1) --> "There is 1 record. It consists of 1 party each."'
' Pluralize(Msg, 6) --> "There are 6 records. They consist of 6 parties each."'
'---------------------------------------------------------------------------------------'
''
Function Pluralize(Text As String, Num As Variant, Optional NumToken As String = "#")
Const OpeningBracket = "\["
Const ClosingBracket = "\]"
Const DividingSlash = "/"
Const CharGroup = "([^\]]*)" 'Group of 0 or more characters not equal to closing bracket'
Dim IsPlural As Boolean, Msg As String, Pattern As String
On Error GoTo Err_Pluralize
If IsNumeric(Num) Then
IsPlural = (Num <> 1)
End If
Msg = Text
'Replace the number token with the actual number'
Msg = Replace(Msg, NumToken, Num)
'Replace [y/ies] style references'
Pattern = OpeningBracket & CharGroup & DividingSlash & CharGroup & ClosingBracket
Msg = RegExReplace(Pattern, Msg, "$" & IIf(IsPlural, 2, 1))
'Replace [s] style references'
Pattern = OpeningBracket & CharGroup & ClosingBracket
Msg = RegExReplace(Pattern, Msg, IIf(IsPlural, "", ""))
'Return the modified message'
Pluralize = Msg
End Function
Function RegExReplace(SearchPattern As String, _
TextToSearch As String, _
ReplacePattern As String) As String
Dim RE As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.MultiLine = False
.Global = True
.IgnoreCase = False
.Pattern = SearchPattern
End With
RegExReplace = RE.Replace(TextToSearch, ReplacePattern)
End Function
использование было отрезано немного в комментариях кода выше, поэтому я повторю его здесь:
Msg = "There [is/are] # record[s]. [It/They] consist[s/] of # part[y/ies] each."
Pluralize(Msg, 1) --> "There is 1 record. It consists of 1 party each."
Pluralize(Msg, 6) --> "There are 6 records. They consist of 6 parties each."
Да, это решение игнорирует языки, которые не являются английскими. Имеет ли это значение, зависит от ваших требований.
вы можете автоматически генерировать множественное число, см., например. множественное число генератор.
полигамный правила создания см. Википедия
string msg = "Do you want to delete " + numItems + GetPlural(" item", numItems) + "?";
Как насчет более общего способа. Избегайте плюрализации во втором предложении:
Number of selected items to be deleted: noofitemsselected. Are you sure?
я узнаю, что, делая это таким образом, ставит число в конце строки, которое очень легко обнаружить. Это решение будет работать с той же логикой на любом языке.
мой общий подход состоит в том, чтобы написать "одну/множественную функцию", например:
public static string noun(int n, string single, string plural)
{
if (n==1)
return single;
else
return plural;
}
тогда в теле сообщения я вызываю эту функцию:
string message="Congratulations! You have won "+n+" "+noun(n, "foobar", "foobars")+"!";
это не намного лучше, но, по крайней мере, (a) помещает решение в функцию и поэтому немного разглаживает код, и (b) достаточно гибок для обработки нерегулярных множественных чисел. то есть достаточно легко сказать существительное(n, "ребенок", "дети") и тому подобное.
конечно, это работает только для английского, но концепция легко расширяется до языков с более сложными окончаниями.
мне кажется, что вы можете сделать последний параметр необязательным для простого случая:
public static string noun(int n, string single, string plural=null)
{
if (n==1)
return single;
else if (plural==null)
return single+"s";
else
return plural;
}
интернационализации
Я предполагаю, что вам нужна поддержка интернационализации, в этом случае разные языки имеют разные шаблоны для множественных чисел (например, специальная форма множественного числа для 2 чего-то или более сложных языков, таких как польский), и вы не можете полагаться на применение некоторого простого шаблона к вашей строке, чтобы исправить это.
вы можете использовать GNU Gettext ngettext
функция и предоставить два английских сообщения в исходном коде. Gettext предоставит инфраструктуру для выберите из других (потенциально более) сообщений при переводе на другие языки. См.http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html для полного описания множественной поддержки GNU gettext.
GNU Gettext находится под LGPL. ngettext
называется GettextResourceManager.GetPluralString
в порту C# Gettext.
(Если вам не нужна поддержка локализации и вы не хотите сразу использовать Gettext, напишите свою собственную функцию, которая делает это для английского языка, и передайте два полных сообщения к нему, таким образом, если вам нужно l10n позже, вы можете добавить, переписав одну функцию.)
Как насчет написать функцию типа
string GetOutputMessage(int count, string oneItemMsg, string multiItemMsg)
{
return string.Format("{0} {1}", count, count > 1 ? multiItemMsg : oneItemMsg);
}
.. и использовать его, когда понадобится?
string message = "You have selected " + GetOutputMessage(noofitemsselected,"item","items") + ". Are you sure you want to delete it/them?";
для первой проблемы, я имею в виду Pluralize, вы можете использовать Инфлектора.
и во-вторых, вы можете использовать расширение строкового представления с именем, таким как ToPronounString.
у меня был точно такой же вопрос, заданный мне вчера членом нашей команды.
Так как он снова появился здесь, на StackOverflow, я подумал, что Вселенная говорит мне, чтобы иметь баш на производство достойного решения.
я быстро собрал что-то вместе, и это ни в коем случае не идеально, однако это может быть полезно или вызвать некоторое обсуждение/развитие.
этот код основан на идее, что может быть 3 сообщения. Один для нулевых элементов, один для одного элемента и один для более чем одного элемента, которые следуют следующей структуре:
singlePropertyName
singlePropertyName_Zero
singlePropertyName_Plural
Я создал внутренний класс для тестирования, чтобы имитировать класс ресурсов. Я еще не тестировал это, используя фактический файл ресурсов, поэтому я еще не вижу полного результата.
вот код (в настоящее время я включил некоторые дженерики, где я знаю, что мог бы указать третий параметр просто как тип, а также второй параметр-это строка, я думаю, что есть способ объединить эти два параметра во что-то лучшее, но я вернусь к этому, когда у меня будет свободный момент.
public static string GetMessage<T>(int count, string resourceSingularName, T resourceType) where T : Type
{
var resourcePluralName = resourceSingularName + "_Plural";
var resourceZeroName = resourceSingularName + "_Zero";
string resource = string.Empty;
if(count == 0)
{
resource = resourceZeroName;
}
else{
resource = (count <= 1)? resourceSingularName : resourcePluralName;
}
var x = resourceType.GetProperty(resource).GetValue(Activator.CreateInstance(resourceType),null);
return x.ToString();
}
тест класс ресурса:
internal class TestMessenger
{
public string Tester{get{
return "Hello World of one";}}
public string Tester_Zero{get{
return "Hello no world";}}
public string Tester_Plural{get{
return "Hello Worlds";}}
}
и мой быстрый метод выполнения
void Main()
{
var message = GetMessage(56, "Tester",typeof(TestMessenger));
message.Dump();
}
с моей точки зрения, ваше первое решение является наиболее подходящим. Почему я говорю, что, если вам нужно приложение для поддержки нескольких языков, второй вариант может быть кропотливым. С помощью кулачкового подхода легко локализовать текст без особых усилий.
вы можете перейти на более общее сообщение, например "вы уверены, что хотите удалить выбранный элемент(ы)".
Я зависит от того, насколько хорошее сообщение, которое вы хотите иметь. От простого к сложному:
перепишите сообщение об ошибке, чтобы избежать плюрализации. Не так приятно для пользователя, но быстрее.
используйте более общий язык, но все равно включите число(ы).
используйте" плюрализацию " и систему перегибов Ala Rails, чтобы вы могли сказать
pluralize(5,'bunch')
и вам5 bunches
. Рельсы имеют хорошую картину для этот.для интернационализации вам нужно посмотреть, что предоставляет Java. Это будет поддерживать широкий спектр языков, включая те, которые имеют различные формы прилагательных с 2 или 3 элементами. Решение " s " очень ориентировано на английский язык.
какой вариант вы идете, зависит от ваших целей продукт. - НДП
Почему вы хотите представить сообщение, которое пользователи могут понять? Это противоречит 40-летней истории программирования. Не-е-ет, у нас все хорошо, не порть все понятно сообщения.
(j/k)
сделайте это так, как это делается в World of Warcraft:
BILLING_NAG_WARNING = "Your play time expires in %d |4minute:minutes;";
становится немного короче с
string message = "Are you sure you want to delete " + noofitemsselected + " item" + (noofitemsselected>1 ? "s" : "") + "?";
одним из подходов, который я не видел, было бы использование тега подстановки / выбора (например, что-то вроде "Вы собираетесь раздавить {0} [?i ({0}=1): / кактус/кактусы/]". (другими словами, есть форматное выражение, указывающее подстановку, основанную на том, равен ли нулевой аргумент, взятый как целое число, 1). Я видел такие теги, используемые в дни до .net; я не знаю какого-либо стандарта для них в .net, и я не знаю лучшего способа их форматирования.
Я бы подумал из коробки на минуту, все предложения здесь либо сделать плюрализацию (и беспокоиться о более чем 1 уровне плюрализации, пола и т. д.), либо не использовать его вообще и обеспечить хорошую отмену.
Я бы пошел не языковым путем и использовал визуальные очереди для этого. например, представьте себе приложение для Iphone, которое вы выбираете, вытирая палец. перед их удалением с помощью кнопки master delete он" встряхнет " выбранные элементы и покажет вам знак вопроса под названием поле с кнопками V (ok) или X (отмена)...
или в 3D-мире Kinekt / Move / Wii - представьте, что вы выбираете файлы, перемещаете руку к кнопке удаления и вам говорят, чтобы переместить руку над головой, чтобы подтвердить (используя те же визуальные символы, что и я упоминал ранее. например, вместо того, чтобы просить вас удалить 3 файла? он покажет вам 3 файла с зависающим полупрозрачным красным X и скажет вам сделать что-то, чтобы подтвердить.