多个用户消息

Øyv*_*hen 106 c# string localization pluralize

很多时候,当生成要向用户显示的消息时,该消息将包含一些我想要通知客户的内容.

我举一个例子:客户从1开始选择了多个项目,并点击了删除.现在我想给客户一个确认信息,我想提一下他选择的项目数量,以便通过选择一堆项目并在他只想删除其中一项时单击删除来最大限度地减少他犯错误的可能性.他们.

一种方法是制作如下通用消息:

int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " item(s). Are you sure you want to delete it/them?";
Run Code Online (Sandbox Code Playgroud)

"问题"这里是哪里的情况下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") + "?";
Run Code Online (Sandbox Code Playgroud)

如果代码中有许多对数字的引用,并且实际的消息难以阅读,那么这会非常长且非常讨厌.

所以我的问题很简单.有没有更好的方法来生成这样的消息?

编辑

我看到很多人在我提到消息应该显示在消息框中的情况下已经非常挂断了,并且简单地给出了如何避免使用消息框的答案,这一切都很好.

但请记住,除了消息框之外,复数化问题也适用于程序中其他地方的文本.例如,在显示网格中选择的行数的网格旁边的标签将具有与复数相同的问题.

所以这基本上适用于从程序中以某种方式输出的大多数文本,然后解决方案就不再那么简单,只需将程序更改为不再输出文本:)

Roa*_*ior 94

您可以通过删除没有任何消息的项目并为用户提供非常好的撤消功能来避免所有这些混乱的数据.用户从不读任何东西.无论如何,你应该建立一个良好的撤销设施作为你的程序的一部分.

当您创建一个全面的撤消设施时,您实际上可以获得2个好处.第一个好处是让用户改变错误并尽量减少阅读,从而使用户的生活更轻松.第二个好处是,您的应用程序通过允许逆转非平凡的工作流程(而不仅仅是错误)来反映现实生活.

我曾经在不使用单个对话框或确认消息的情况下编写应用程序.它花了一些认真的思考,并且比使用确认类型的消息更难实现.但最终结果是根据其最终用户使用相当不错.

  • 我没有读它就赞成这个.我总是可以在以后撤消它 (50认同)
  • 出于某种原因,我真的觉得我同意这一点并且应该支持它. (12认同)
  • @Robert Harvey:可能是伪造的,但多年前我读到Lotus已经为Mac发送了40,000单位Lotus Notes,并且返回了60,000单位.界面非常糟糕,甚至盗版的人都把它送回去了. (6认同)
  • 可辩论,但无论如何,除了删除确认消息之外,还会出现同样的问题,因此这个答案与问题完全相同. (5认同)
  • @Øyvind,原因如下:要求用户验证的消息对用户来说是昂贵的,特别是当它们以模式对话框的形式呈现时*停止一切*直到用户回答对话框.这让很多用户感到恼火,他们开始只需点击任何按钮就可以通过对话框(你刚刚点击了多少安装人员*下一个,下一个,下一个?*).因此,它更安全,如果您只是进行软删除,并提供撤消功能,则用户摩擦力会降低.Gmail使用此技术效果很好. (4认同)
  • @webbiedave - 相反,如果程序在删除后显然显示删除,则确认消息只会妨碍并且通常不会*完成. (2认同)
  • 对于某些进程,出于法律原因,您无法创建撤消. (2认同)

sle*_*man 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?"
);
Run Code Online (Sandbox Code Playgroud)

这是因为不同的语言处理多个不同的.像马来语这样的人甚至没有语法复数,因此字符串通常是相同的.分离这两个字符串可以在以后更容易地支持其他语言.

否则,如果这个应用程序是由一般公众使用并且应该是用户友好的,那么第二种方法是更可取的.对不起,我真的不知道这样做的简短方法.

如果这个应用程序只能由您的公司内部使用,那么请执行快捷方式"item(s)".在编写企业代码时,你真的不必给任何人留下深刻的印象.但我建议不要为公开使用的应用程序执行此操作,因为这会给人一种程序员懒惰的印象,从而降低他们对应用程序质量的看法.相信我,像这件小事.

  • 虽然我同意这个前提,但你忽略了具有两个以上多元度的语言.(以俄语为例.三种不同的说法取决于它是1,<5还是> = 5,甚至取决于你究竟在说什么).基本上我说你需要一个更强的条件语句,而不仅仅是一个三元运算符. (34认同)
  • 虽然我们在这里,希伯来语可以有1,2,3-10和11+的不同多个. (15认同)
  • 你甚至可以在这个例子中优化掉'noofitemsselected'作为第一个选项; 因为你知道它是1. (4认同)
  • @Joel,希伯来语可以有那么多不同的多元化?לאידעתי(我不知道)!就复数而言,现代希伯来语就像英语(虽然*古代*希伯来语也有双重形式.) (3认同)
  • 在英语中,零被视为复数(0项,2项); 在希伯来语,俄语,罗马尼亚语中会发生什么?有没有一种简单的方法来识别范围?我猜测必须根据语言确定信息,因此任何基于消息文件的系统都必须处理一组消息中不同数量的多个.哎哟! 设置翻译也很棘手 - 不同语言需要不同数量的消息. (3认同)
  • 如果翻译是个问题,那么您的示例代码也不是最理想的.您对数字"数字+项目"的排序可能不会使用其他语言.解决方案是使用带有string.Format的占位符,如@Dan Puzey所建议的那样 (2认同)

Cod*_*ray 39

怎么样:

string message = "Are you sure you want to delete " + noofitemsselected + " item(s)?"
Run Code Online (Sandbox Code Playgroud)

这样,您就可以消除号码协议的困难,并最终为用户提供更短,更准确的错误消息作为奖励.我们都知道用户无论如何都不会阅读错误消息.它们越短,他们就越有可能至少浏览一下文本.

或者,凭借用户不会阅读错误消息的知识,您可以采用不同的方式.完全跳过确认消息,只提供Just Works的撤消功能,无论删除了什么.大多数用户已经习惯于在他们发现不是他们想要的操作时撤消操作,并且很可能发现这种行为比处理另一个恼人的弹出窗口更自然.


Ber*_*sch 20

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.
Run Code Online (Sandbox Code Playgroud)

在你的情况下,你想要一些更简单的东西:

MessageFormat form = new MessageFormat("Are you sure you want to delete {0,choice,1#one item,1<{0,number.integer} files}?");
Run Code Online (Sandbox Code Playgroud)

这种方法的优点是它可以很好地与i18n包一起使用,并且您可以为没有复数或单数词概念的语言(如日语)提供正确的翻译.


Jen*_*ens 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?";
Run Code Online (Sandbox Code Playgroud)

然后将它们送入String.Format之类

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

string message = String.Format(messageTemplate, noofitemsselected)
Run Code Online (Sandbox Code Playgroud)

我认为这种方法更容易本地化和维护.所有UI消息都位于单个位置.


gde*_*ohn 11

您可以通过不同地表达消息来完全回避问题.

string message = "The number of selected items is " + noofitemsselected + ". Are you sure you want to delete everything in this selection?";
Run Code Online (Sandbox Code Playgroud)


Dan*_*zey 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);
Run Code Online (Sandbox Code Playgroud)

此外,在WPF应用程序中,我见过系统,其中资源字符串将被管道分隔以具有两个消息:单个和多个消息(或者甚至是零/单个/多个消息).然后可以使用自定义转换器来解析此资源并使用相关的(格式化的)字符串,因此您的Xaml是这样的:

<TextBlock Text="{Binding numOfItems, Converter={StaticResource c:NumericMessageFormatter}, ConverterParameter={StaticResource s:SuitableMessageTemplate}}" />
Run Code Online (Sandbox Code Playgroud)


smi*_*man 7

对于英语,上面有很多答案.对于其他语言来说,它更加困难,因为复数取决于名词的性别和结尾这个词.法语中的一些例子:

经常男性化:

Vous avez choisi 1 compte. Voulez-vous vraiment le supprimer.
Vous avez choisi 2 comptes. Voulez-vous vraiment les supprimer.
Run Code Online (Sandbox Code Playgroud)

经常女人味

Vous avez choisi 1 table. Voulez-vous vraiment la supprimer.
Vous avez choisi 2 tables. Voulez-vous vraiment les supprimer.
Run Code Online (Sandbox Code Playgroud)

不规则的男性化(用's'完成)

Vous avez choisi 1 pays. Voulez-vous vraiment le supprimer.
Vous avez choisi 2 pays. Voulez-vous vraiment les supprimer?
Run Code Online (Sandbox Code Playgroud)

在大多数拉丁语言中存在同样的问题,并且在德语或俄语中变得更糟,其中有3种性别(maculine,女性和中性).

如果你的目标是处理的不仅仅是英语,你需要小心.


Dav*_*cic 5

为了能够有多个可以正确本地化的消息,我的观点是,首先在数字和消息之间创建一个间接层是明智的.

例如,使用某种常量来指定要显示的消息.使用一些隐藏实现细节的函数来获取消息.

get_message(DELETE_WARNING, quantity)
Run Code Online (Sandbox Code Playgroud)

接下来,创建一个包含可能的消息和变体的字典,并使变体知道何时应该使用它们.

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?'
}
Run Code Online (Sandbox Code Playgroud)

现在您可以简单地找到与该消息对应的键quantity并插入该quantity消息的值.

这个过于简单和天真的例子,但我真的没有看到任何其他理智的方式,并能够为L10N和I18N提供良好的支持.


mwo*_*e02 5

您必须将以下函数从VBA转换为C#,但您的使用将更改为:

int noofitemsselected = SomeFunction();
string message = Pluralize("You have selected # item[s]. Are you sure you want to delete [it/them]?", noofitemsselected);
Run Code Online (Sandbox Code Playgroud)

我有一个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
Run Code Online (Sandbox Code Playgroud)

在上面的代码注释中,使用情况有所减少,所以我将在此处重复:

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."
Run Code Online (Sandbox Code Playgroud)

是的,此解决方案忽略了非英语语言.这是否重要取决于您的要求.