2010-11-23 2 views
105

Много раз при генерации сообщений, отображаемых пользователю, сообщение будет содержать число что-то, о котором я хочу сообщить клиенту.Множественность в сообщениях пользователя

Я приведу пример: клиент выбрал несколько элементов от 1 до и нажал кнопку «Удалить». Теперь я хочу дать подтверждение клиенту, и я хочу упомянуть количество элементов, которые он выбрал, чтобы свести к минимуму вероятность того, что он допустил ошибку, выбрав кучу элементов и нажав кнопку delete, когда он только хочет удалить одну из их.

Один из способов сделать общее сообщение вроде этого:

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") + "?"; 

Это получает довольно долго и довольно неприятный очень быстро, если есть много ссылок на множество чисел внутри кода, и фактическое сообщение становится трудно читать.

Так что мои вопросы просто. Есть ли лучшие способы генерации таких сообщений?

EDIT

Я вижу много людей уже получили очень повесил трубку в том случае, когда я говорил о том, что сообщение должно отображаться в окне сообщения, а просто дал ответ о том, как избежать использования ящик сообщений вообще, и это все хорошо.

Но помните, что проблема плюрализации также применяется к текстам других мест в программе в дополнение к ящикам сообщений. Например, метка рядом с сеткой, отображающей количество строк, выбранных в сетке, будет иметь одинаковую проблему с плюрализацией.

Так что это в основном применяется для большинства текста, выведенный в некотором роде из программ, а затем решение не так просто, как просто изменить программу, чтобы не выводится текст больше :)

+0

+1 Хороший вопрос – abhilash 2010-11-23 08:40:40

+5

@ 0xA3: Я действительно не знаю, имеет ли каждый язык плюрализацию, легко выражается как «элемент (ы)». – Jens 2010-11-23 09:02:08

ответ

53

Если нет когда-либо какой-то шанс, независимо от того, насколько она мала, что это приложение должны быть переведены на другие языки, тогда оба они ошибаются. Правильный способ сделать это:

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)". Вы не должны никого никого не впечатлять при написании кода предприятия. Но я бы посоветовал не делать этого для публично потребляемого приложения, потому что это создает впечатление, что программист ленив и тем самым снижает свое мнение о качестве приложения. Поверьте мне, мелочи вроде этого.

+0

+1 Это действительно лучший ответ. – 2010-11-23 09:02:16

+34

Хотя я согласен с предпосылкой, вы игнорируете языки с более чем двумя степенями множественности. (Возьмите, например, русский, три разных способа сказать, в зависимости от того, есть ли его 1, <5, or > = 5, и даже это зависит от того, что именно вы говорите). В основном я говорю, что вам нужно более сильное условное утверждение, а не просто троичный оператор. – crasic 2010-11-23 09:20:24

+0

Я должен согласиться с Коуди здесь. Действительно хороший ответ. +1, но я подожду некоторое время, чтобы сделать его принятым ответом, если появятся другие ответы. – 2010-11-23 10:18:35

2

С моей точки зрения , ваше первое решение является наиболее подходящим. Почему я говорю, что, если вам нужно приложение для поддержки нескольких языков, второй вариант может быть кропотливым. Благодаря первому подходу легко локализовать текст без особых усилий.

2

Вы можете найти более общее сообщение типа «Вы действительно хотите удалить выбранные элементы».

+3

Это, безусловно, работает, но я думаю, что идея афера о том, чтобы показать количество предметов, которые должны быть удалены с первого взгляда, является хорошей.Не раз я пытался удалить файл с рабочего стола и случайно удалил более одного файла. – 2010-11-23 08:51:49

39

Как насчет:

string message = "Are you sure you want to delete " + noofitemsselected + " item(s)?" 

Таким образом, вы устранить трудности, номер договора, и в конечном итоге с еще более коротким, более сообщение об ошибке в к-точке для пользователя в качестве бонуса. Мы все знаем users don't read error messages anyway. Чем короче они, тем более вероятно, что они должны быть как минимум взгляд на текст.

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

+1

Хороший ответ. Однако в этом случае заказчик запросил сообщения об ошибках для многих случаев, поскольку считал, что это лучший подход. Но изменение текста может по крайней мере минимизировать проблему и сделать ее менее беспорядочной. – 2010-11-23 09:44:09

+0

@ Øyvind: достаточно справедливо. Поскольку вы должны следовать за клиентом, я либо возьму первый подход, который я предложил, либо использовал файл ресурсов с функцией обработчика. Я просто подумал, что стоит указать альтернативу, потому что ее так легко забыть. Черт, я даже не думал об этом, пока не представил свой первый ответ. – 2010-11-23 09:49:14

+0

Альтернативы приветствуются. Вот почему я дал ему +1 тоже :) – 2010-11-23 09:53:40

11

Вы можете полностью обойти проблему, сформулировав сообщение по-разному.

string message = "The number of selected items is " + noofitemsselected + ". Are you sure you want to delete everything in this selection?"; 
0

Это становится немного короче с

string message = "Are you sure you want to delete " + noofitemsselected + " item" + (noofitemsselected>1 ? "s" : "") + "?"; 
+2

Немного короче, но так же нечитабельно;) – 2010-11-23 09:49:55

4

Вы можете сгенерировать множественном автоматически, см, например. plural generator.

Для множества правил генерирующих см wikipedia

string msg = "Do you want to delete " + numItems + GetPlural(" item", numItems) + "?"; 
+0

Мне нравится использовать этот метод, если приложение не интернационализировано. Puralizing приводит к более отполированному применению. – cspolton 2010-11-23 08:54:29

+1

Его действительно трудно интернационализировать это, и он часто терпит неудачу на английском языке, например. «У вас есть заказы + numMice + GetPlural (« мышь », numMice)» – 2010-11-23 09:08:59

+0

@ Джеймс Андерсон: Теперь это не лучший пример, грамматически это :-) Не попадайте в ловушку мышления о том, что использование метода GetPural метод * only *, который вы можете использовать. – cspolton 2010-11-23 10:21:52

3

Как насчет писать функции, как

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?"; 
+3

вы не думали о том, что вы сделали LOL, у вас есть функция, чтобы разобраться в множителях или нет для предложения, тогда вы нажмете «it/them» на конце lol – Barkermn01 2010-11-23 10:45:31

3

Для первой задачи, я имею в виду множественном числе, вы можете использовать Inflector.

И для второго вы можете использовать расширение строкового представления с таким именем, как ToPronounString.

10

Первое, что я предлагаю: использовать 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}}" /> 
12

Я бы не стал жестко кодировать сообщение, но предоставлял два сообщения в отдельном файле ресурсов. Как

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?"; 

, а затем кормить их в String.Format как

if(noofitemsselected == 1) 
    messageTemplate = MessageResources.DELETE_SINGLE; 
else 
    messageTemplate = MessageResources.DELETE_MULTI; 

string message = String.Format(messageTemplate, noofitemsselected) 

Я думаю, что этот подход легче локализовать и поддерживать. Все сообщения пользовательского интерфейса будут находиться в одном месте.

94

Вы можете избежать всего этого беспорядочного множественного числа, просто удалив элементы без какого-либо сообщения и предоставив пользователю действительно хорошее средство Отменить. Users never read anything. Вы, конечно, should build a good Undo facility как часть вашей программы.

Вы получаете 2 преимущества при создании всеобъемлющего средства Отменить. Первое преимущество облегчает жизнь пользователя, позволяя ему/ей преодолевать ошибки и минимизировать чтение. Второе преимущество заключается в том, что ваше приложение отражает реальную жизнь, позволяя отменять нетривиальный рабочий процесс (а не только ошибки).

Я как-то написал приложение, не используя ни одного диалога или подтверждающего сообщения. Потребовалось некоторое серьезное мышление и было значительно сложнее реализовать, чем использование сообщений типа подтверждения. Но конечный результат был довольно приятным для использования в соответствии с его конечными пользователями.

+12

По какой-то причине, я действительно чувствую, что согласен с этим и должен его продвигать. – 2010-11-23 09:14:47

4

Как насчет более общего пути. Избегайте плюрализацию во втором предложении:

Number of selected items to be deleted: noofitemsselected. 
Are you sure?

Я считаю, что делает его таким образом, ставит число в конце линии, которая на самом деле легко обнаружить. Это решение будет работать с той же логикой на любом языке.

3

У меня был такой же вопрос, заданный мне вчера членом нашей команды.

С тех пор, как он появился снова на StackOverflow, я понял, что вселенная говорила мне, что у меня есть бат при создании приличного решения.

Я быстро поставил что-то вместе, и это ни в коем случае не идеально, но это может быть полезно или вызвать некоторые обсуждения/разработки.

Этот код основан на идее, что сообщения могут быть 3. Один ноль элементов, один для одного элемента и один для более чем одного элемента, которые следуют следующую структуру:

singlePropertyName
singlePropertyName_Zero
singlePropertyName_Plural

Я создал внутренний класс для тестирования с тем чтобы mimick класс ресурсов. Я не тестировал это, используя фактический файл ресурсов, но я пока не вижу полного результата.

Вот код (в настоящее время я включил некоторые дженерики, где я знаю, что мог бы указать третий параметр просто как Тип, а также второй параметр - это строка, я думаю, что есть способ объединить эти два параметра во что-то лучше, но я вернусь к этому, когда у меня будет свободный момент.

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(); 
} 
7

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

Обычный мужской:

Vous avez choisi 1 compte. Voulez-vous vraiment le supprimer. 
Vous avez choisi 2 comptes. Voulez-vous vraiment les supprimer. 

Regular женственный

Vous avez choisi 1 table. Voulez-vous vraiment la supprimer. 
Vous avez choisi 2 tables. Voulez-vous vraiment les supprimer. 

Нерегулярное мужского (заканчивается 'с')

Vous avez choisi 1 pays. Voulez-vous vraiment le supprimer. 
Vous avez choisi 2 pays. Voulez-vous vraiment les supprimer? 

Та же проблема существует в большинстве Латинских языках и ухудшается на немецком или русском языках, где есть 3 пола (maculine, feminine и стерилизуют).

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

19

Как насчет того, что Java имела в течение многих лет: java.text.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, и вы можете обеспечить перевод должным образом для языков (например, японский) которые не имеют понятия множественных или сингулярных слов.

2

В зависимости от того, насколько хорошо сообщение вы хотите иметь. От самых простых до самых сложных:

  1. Повторно написать сообщение об ошибке, чтобы избежать плюрализации. Не так хорошо для вашего пользователя, но быстрее.

  2. Используйте более общий язык, но по-прежнему включайте номера (номера).

  3. Используйте систему плюрализации и инфлектора ala Rails, поэтому вы можете сказать pluralize(5,'bunch') и получить 5 bunches. Для этого у Rails есть хорошая схема.

  4. Для интернационализации вам необходимо посмотреть, что предоставляет Java. Это будет поддерживать широкий спектр языков, включая те, которые имеют разные формы прилагательных с 2 или 3 предметами. Решение «s» очень ориентировано на английский язык.

Какой вариант вы используете, зависит от ваших целей продукта. - ndp

5

Чтобы иметь возможность иметь множественные сообщения, которые можно локализовать правильно, я считаю, что было бы разумно сначала создать слой косвенности между номером и сообщением.

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

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.

0

Один из подходов, о которых я не упоминал, будет использовать тег подстановки/выбора (например, что-то вроде «Вы собираетесь скворовать {0} [? I ({0} = 1):/cactus/cacti /] "(другими словами, иметь форматируемое выражение, указать подстановку, основанную на том, равен ли аргумент нулю, принятый как целое число, 1). Я видел такие теги, используемые в дни перед .net; не знаю ни одного стандарта для них в .net, и я не знаю, лучший способ форматирования их

5

Вы должны будете переводить функцию ниже из 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, "$1", "")) 

    '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." 

Да, это решение игнорирует языки которые не являются английскими. Неважно, зависит ли это от ваших требований.

4

Мой общий подход заключается в написании «единый/мн функцию», как это:

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")+"!"; 

Это ISN» t намного лучше, но, по крайней мере, он (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; 
} 
4

Интернационализация

Я предполагаю, что вы хотите поддержку интернационализации, в этом случае на разных языках имеют разные шаблоны для множественных (напримерспециальная форма множественного числа для 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 позже, вы можете добавить, переписав одну функцию.)

2

Почему вы хотите представить сообщение, которое пользователи могут реально понять? Это связано с 40-летней историей программирования. Nooooo, у нас все хорошо, не портите его понятно сообщений.


(дж/к)

2

Делают это, как это сделано в World Of Warcraft:

BILLING_NAG_WARNING = "Your play time expires in %d |4minute:minutes;"; 
0

Я думаю, что из коробки на минуту, все предложения здесь либо сделать плюрализацию (и беспокоиться о более чем 1 уровне плюрализации, пола и т. д.), либо не использовать его вообще, и обеспечить приятную отмену.

Я бы пошел неязычным способом и использовал визуальные очереди для этого. например представьте себе приложение Iphone, которое вы выбираете, вытирая палец. перед удалением их с помощью кнопки «Мастер удаления» она «встряхнет» выбранные элементы и покажет вам поле с вопросительным знаком с кнопками V (ok) или X (отмена) ...

Или, в 3D-мире из Kinekt/Move/Wii - представьте, что вы выбираете файлы, перемещая руку к кнопке удаления, и вам говорят, чтобы вы двигали рукой над головой, чтобы подтвердить (используя те же визуальные символы, что и раньше, например, вместо того, чтобы просить вас удалить 3 файла ? он покажет вам 3 файла с парящим полупрозрачным красным X и скажет вам сделать что-то, чтобы подтвердить.

Смежные вопросы